diff --git a/.changeset/thirty-spoons-call.md b/.changeset/thirty-spoons-call.md new file mode 100644 index 00000000000..4853b541b98 --- /dev/null +++ b/.changeset/thirty-spoons-call.md @@ -0,0 +1,8 @@ +--- +"graphiql": major +"@graphiql/react": minor +--- + +- new looks of tabs + +- fix `disableTabs` when `Add tab` button is still shown diff --git a/packages/graphiql-plugin-code-exporter/src/index.css b/packages/graphiql-plugin-code-exporter/src/index.css index 716d01d954c..16af4b1b6f9 100644 --- a/packages/graphiql-plugin-code-exporter/src/index.css +++ b/packages/graphiql-plugin-code-exporter/src/index.css @@ -133,7 +133,7 @@ } & .CodeMirror { box-shadow: var(--popover-box-shadow); - border-radius: calc(var(--border-radius-12)); + border-radius: var(--border-radius-12); padding: var(--px-16); } } diff --git a/packages/graphiql-react/package.json b/packages/graphiql-react/package.json index 205548dc1f1..77b14bb8b4f 100644 --- a/packages/graphiql-react/package.json +++ b/packages/graphiql-react/package.json @@ -59,7 +59,7 @@ "codemirror": "^5.65.3", "codemirror-graphql": "^2.0.13", "copy-to-clipboard": "^3.2.0", - "framer-motion": "^6.5.1", + "framer-motion": "^10.0.0", "graphql-language-service": "^5.2.2", "markdown-it": "^14.1.0", "set-value": "^4.1.0" diff --git a/packages/graphiql-react/src/style/root.css b/packages/graphiql-react/src/style/root.css index 5f18fa336c6..8c8dbcfa09f 100644 --- a/packages/graphiql-react/src/style/root.css +++ b/packages/graphiql-react/src/style/root.css @@ -68,7 +68,7 @@ /* Layout */ --sidebar-width: 60px; --toolbar-width: 40px; - --session-header-height: 51px; + --session-header-height: 38.5px; } @media (prefers-color-scheme: dark) { diff --git a/packages/graphiql-react/src/ui/tabs.css b/packages/graphiql-react/src/ui/tabs.css index d3ddb46e7e8..66cd5b73361 100644 --- a/packages/graphiql-react/src/ui/tabs.css +++ b/packages/graphiql-react/src/ui/tabs.css @@ -1,46 +1,84 @@ .graphiql-tabs { + --bg: hsl(var(--color-base)); + display: flex; align-items: center; - overflow-x: auto; - padding: var(--px-12); + gap: var(--px-8); + /* reset browser defaults */ + padding: 0; + margin: 0; + list-style: none; +} - & > :not(:first-child) { - margin-left: var(--px-12); - } +/* trick to shrink multiple tabs, instead of overflow container */ +.graphiql-tabs, +.graphiql-tab { + min-width: 0; } .graphiql-tab { - align-items: stretch; - border-radius: var(--border-radius-8); - color: hsla(var(--color-neutral), var(--alpha-secondary)); + border-radius: var(--border-radius-8) var(--border-radius-8) 0 0; + background: hsla(var(--color-neutral), var(--alpha-background-light)); + position: relative; display: flex; + max-width: 140px; - & > button.graphiql-tab-close { - visibility: hidden; - } - &.graphiql-tab-active > button.graphiql-tab-close, - &:hover > button.graphiql-tab-close, - &:focus-within > button.graphiql-tab-close { - visibility: unset; + /* disable shrinking while changing the operation name */ + &:not(:focus-within) { + transform: none !important; } + &:hover, + &:focus-within, &.graphiql-tab-active { - background-color: hsla(var(--color-neutral), var(--alpha-background-heavy)); - color: hsla(var(--color-neutral), 1); + background: var(--bg); + color: hsl(var(--color-neutral)); + + .graphiql-tab-close { + display: block; + } } -} -button.graphiql-tab-button { - padding: var(--px-4) 0 var(--px-4) var(--px-8); -} + .graphiql-tab-button { + border-radius: var(--border-radius-8) var(--border-radius-8) 0 0; + overflow: hidden; + text-overflow: ellipsis; + padding: var(--px-4) 28px var(--px-4) var(--px-8); -button.graphiql-tab-close { - align-items: center; - display: flex; - padding: var(--px-4) var(--px-8); + &:hover { + background: none; + } + } + + .graphiql-tab-close { + position: absolute; + right: min(var(--px-4), 5%); + top: 50%; + transform: translateY(-50%); + display: none; + background: var(--bg); + box-shadow: -10px 0 10px 0 var(--bg); + padding: var(--px-6); + line-height: 0; + + & > svg { + height: var(--px-8); + width: var(--px-8); + } + + &:hover { + background: var(--bg); + color: hsl(var(--color-neutral)); + overflow: hidden; /* bg in `:before` will not overflow from radius area */ - & > svg { - height: var(--px-8); - width: var(--px-8); + /* trick to add 2nd bg with opacity */ + &:before { + content: ''; + position: absolute; + inset: 0; + z-index: -1; + background: hsla(var(--color-neutral), 0.3); + } + } } } diff --git a/packages/graphiql-react/src/ui/tabs.tsx b/packages/graphiql-react/src/ui/tabs.tsx index cc5947f13c1..dfbaa793975 100644 --- a/packages/graphiql-react/src/ui/tabs.tsx +++ b/packages/graphiql-react/src/ui/tabs.tsx @@ -2,9 +2,7 @@ import { forwardRef, ReactNode } from 'react'; import { clsx } from 'clsx'; import { Reorder } from 'framer-motion'; import { CloseIcon } from '../icons'; -import { createComponentGroup } from '../utility/component-group'; import { UnStyledButton } from './button'; -import { Tooltip } from './tooltip'; import './tabs.css'; @@ -21,7 +19,7 @@ const TabRoot = forwardRef( {...props} ref={ref} value={value} - aria-selected={isActive ? 'true' : undefined} + aria-selected={isActive} role="tab" className={clsx( 'graphiql-tab', @@ -38,36 +36,34 @@ TabRoot.displayName = 'Tab'; const TabButton = forwardRef< HTMLButtonElement, JSX.IntrinsicElements['button'] ->((props, ref) => ( +>(({ children, className, ...props }, ref) => ( - {props.children} + {children} )); TabButton.displayName = 'Tab.Button'; const TabClose = forwardRef( (props, ref) => ( - - - - - + + + ), ); TabClose.displayName = 'Tab.Close'; -export const Tab = createComponentGroup(TabRoot, { +export const Tab = Object.assign(TabRoot, { Button: TabButton, Close: TabClose, }); diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index cbc85c95775..a2757cca3b4 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -456,18 +456,7 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { } }, []); - const addTab = ( - - - - - ); + const hasMultipleTabs = editorContext.tabs.length > 1; const className = props.className ? ` ${props.className}` : ''; @@ -556,48 +545,55 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) {
{!props.disableTabs && ( - - {editorContext.tabs.length > 1 && ( - <> + <> + {hasMultipleTabs && ( + {editorContext.tabs.map((tab, index) => ( - - { - executionContext.stop(); - editorContext.changeTab(index); - }} + + - {tab.title} - - { - if (editorContext.activeTabIndex === index) { + { executionContext.stop(); - } - editorContext.closeTab(index); - }} - /> - + editorContext.changeTab(index); + }} + > + {tab.title} + + { + if (editorContext.activeTabIndex === index) { + executionContext.stop(); + } + editorContext.closeTab(index); + }} + /> + + ))} - {addTab} - + )} - + + + + + )} -
- {editorContext.tabs.length === 1 && addTab} - {logo} -
+ {logo}
svg { - color: hsla(var(--color-neutral), var(--alpha-secondary)); - display: block; - height: var(--px-16); - width: var(--px-16); -} - -/* The right-hand-side of the session header */ -.graphiql-container .graphiql-session-header-right { - align-items: center; - display: flex; + & > svg { + color: hsla(var(--color-neutral), var(--alpha-secondary)); + display: block; + height: var(--px-16); + width: var(--px-16); + } } /* The GraphiQL logo */ @@ -98,13 +96,15 @@ button.graphiql-tab-add > svg { color: hsla(var(--color-neutral), var(--alpha-secondary)); font-size: var(--font-size-h4); font-weight: var(--font-weight-medium); - padding: var(--px-12) var(--px-16); } /* Undo default link styling for the default GraphiQL logo link */ .graphiql-container .graphiql-logo .graphiql-logo-link { color: hsla(var(--color-neutral), var(--alpha-secondary)); text-decoration: none; + &:focus { + outline: hsla(var(--color-neutral), var(--alpha-background-heavy)) auto 1px; + } } /* The editor of the session */ @@ -117,17 +117,13 @@ button.graphiql-tab-add > svg { /* All editors (query, variable, headers) */ .graphiql-container .graphiql-editors { background-color: hsl(var(--color-base)); - border-radius: calc(var(--border-radius-12)); + border-radius: var(--border-radius-12); box-shadow: var(--popover-box-shadow); display: flex; flex: 1; flex-direction: column; } -.graphiql-container .graphiql-editors.full-height { - margin-top: calc(var(--px-8) - var(--session-header-height)); -} - /* The query editor and the toolbar */ .graphiql-container .graphiql-query-editor { border-bottom: 1px solid diff --git a/yarn.lock b/yarn.lock index 47656fe0296..ba0ea8e4bb3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3160,59 +3160,6 @@ resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz#1fead437f3957ceebe2e8c3f46beccdb9bc575b8" integrity sha512-EWUguj2kd7ldmrF9F+vI5hUOralPd+sdsUnYbRy33vZTuZkduC1shE9TtEMEjAQwyfyMb4ole5KtjF8MsnQOlA== -"@motionone/animation@^10.12.0": - version "10.15.1" - resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.15.1.tgz#4a85596c31cbc5100ae8eb8b34c459fb0ccf6807" - integrity sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ== - dependencies: - "@motionone/easing" "^10.15.1" - "@motionone/types" "^10.15.1" - "@motionone/utils" "^10.15.1" - tslib "^2.3.1" - -"@motionone/dom@10.12.0": - version "10.12.0" - resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.12.0.tgz#ae30827fd53219efca4e1150a5ff2165c28351ed" - integrity sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw== - dependencies: - "@motionone/animation" "^10.12.0" - "@motionone/generators" "^10.12.0" - "@motionone/types" "^10.12.0" - "@motionone/utils" "^10.12.0" - hey-listen "^1.0.8" - tslib "^2.3.1" - -"@motionone/easing@^10.15.1": - version "10.15.1" - resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.15.1.tgz#95cf3adaef34da6deebb83940d8143ede3deb693" - integrity sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw== - dependencies: - "@motionone/utils" "^10.15.1" - tslib "^2.3.1" - -"@motionone/generators@^10.12.0": - version "10.15.1" - resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.15.1.tgz#dc6abb11139d1bafe758a41c134d4c753a9b871c" - integrity sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ== - dependencies: - "@motionone/types" "^10.15.1" - "@motionone/utils" "^10.15.1" - tslib "^2.3.1" - -"@motionone/types@^10.12.0", "@motionone/types@^10.15.1": - version "10.15.1" - resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.15.1.tgz#89441b54285012795cbba8612cbaa0fa420db3eb" - integrity sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA== - -"@motionone/utils@^10.12.0", "@motionone/utils@^10.15.1": - version "10.15.1" - resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.15.1.tgz#6b5f51bde75be88b5411e084310299050368a438" - integrity sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw== - dependencies: - "@motionone/types" "^10.15.1" - hey-listen "^1.0.8" - tslib "^2.3.1" - "@n1ru4l/push-pull-async-iterable-iterator@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.1.0.tgz#be450c97d1c7cd6af1a992d53232704454345df9" @@ -9713,27 +9660,15 @@ fraction.js@^4.2.0: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== -framer-motion@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" - integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw== +framer-motion@^10.0.0: + version "10.18.0" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-10.18.0.tgz#1f4fc51403996ea7170af885bd44a7079d255950" + integrity sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w== dependencies: - "@motionone/dom" "10.12.0" - framesync "6.0.1" - hey-listen "^1.0.8" - popmotion "11.0.3" - style-value-types "5.0.0" - tslib "^2.1.0" + tslib "^2.4.0" optionalDependencies: "@emotion/is-prop-valid" "^0.8.2" -framesync@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20" - integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA== - dependencies: - tslib "^2.1.0" - fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -10378,11 +10313,6 @@ helpertypes@^0.0.18: resolved "https://registry.yarnpkg.com/helpertypes/-/helpertypes-0.0.18.tgz#fd2bf5d3351cc7d80f7876732361d3adba63e5b4" integrity sha512-XRhfbSEmR+poXUC5/8AbmYNJb2riOT6qPzjGJZr0S9YedHiaY+/tzPYzWMUclYMEdCYo/1l8PDYrQFCj02v97w== -hey-listen@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" - integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== - highlight.js@^10.2.0: version "10.5.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.5.0.tgz#3f09fede6a865757378f2d9ebdcbc15ba268f98f" @@ -14439,16 +14369,6 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -popmotion@11.0.3: - version "11.0.3" - resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9" - integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA== - dependencies: - framesync "6.0.1" - hey-listen "^1.0.8" - style-value-types "5.0.0" - tslib "^2.1.0" - possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -16941,14 +16861,6 @@ style-mod@^4.0.0: resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01" integrity sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw== -style-value-types@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad" - integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA== - dependencies: - hey-listen "^1.0.8" - tslib "^2.1.0" - styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f"