diff --git a/jest.config.js b/jest.config.js index de701cb..63aed4f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,13 +1,20 @@ module.exports = { - preset: 'ts-jest/presets/default-esm', // or other ESM presets - globals: { - 'ts-jest': { - useESM: true, - }, - }, + preset: 'ts-jest/presets/default-esm', moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, testEnvironment: 'node', - transform: {}, -}; \ No newline at end of file + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + useESM: true, + // The repo's tsconfig compiles to CommonJS for the published builds. + // Jest, however, loads test modules as ESM (see the default-esm + // preset), so ts-jest must emit ESM too — otherwise the compiled + // `exports`/`require` references throw "exports is not defined". + tsconfig: { module: 'esnext' }, + }, + ], + }, +}; diff --git a/lerna.json b/lerna.json index 41c2e60..6f9fd42 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": [ "packages/*" ], - "version": "2.1.5" + "version": "2.2.0-alpha.1" } diff --git a/packages/react-ui/UPGRADING.md b/packages/react-ui/UPGRADING.md new file mode 100644 index 0000000..57bd3d6 --- /dev/null +++ b/packages/react-ui/UPGRADING.md @@ -0,0 +1,76 @@ +# Upgrading datocms-react-ui + +## v3.0.0 — Semantic color tokens and dark-mode support + +This release reworks the color system around the host's semantic color +tokens. The CMS now computes a full color palette for the active theme +(including dark mode), and `datocms-react-ui` consumes it directly. All +built-in components automatically adapt to whichever theme the user has +selected. + +### Action required + +**Test your plugin in dark mode before publishing.** Upgrading to v3 opts +your plugin into the host's active theme. If the user has dark mode +enabled, your plugin renders with dark colors. + +Common things to audit: + +- **Hardcoded colors** in your CSS (e.g. `color: #333`, `background: white`). + They won't follow the theme; users in dark mode will see them as-is and + the contrast may break. +- **Hardcoded SVG fills** in custom icons. Use `fill="currentColor"` so they + inherit the surrounding `color`. +- **Custom CSS that mixes library components with your own colors.** Verify + the combinations look right in both themes. + +### What's new + +A new set of CSS custom properties is available inside ``. They +follow the host's active theme: + +- `--color--surface`, `--color--surface-hover`, `--color--surface-muted`, … +- `--color--ink`, `--color--ink-subtle`, `--color--ink-placeholder`, + `--color--ink-accent`, … +- `--color--border`, `--color--border-hover` +- Per-context variants: `--color--primary--surface`, `--color--primary--ink`, + `--color--tinted--surface`, `--color--accent--ink`, + `--color--selected--surface`, `--color--disabled--surface`, + `--color--danger--surface`, … +- Feedback: `--color--feedback-fail--ink`, + `--color--feedback-warning--surface`, `--color--feedback-success--ink`, … +- Plus diff, status, overlay, stacked, progress, tooltip, code and shadow + groups. + +See the `Canvas` JSDoc for the full reference. + +All built-in components (`Button`, `Dropdown`, `Section`, `TextInput`, +`SwitchInput`, `Toolbar`, `Tooltip`, …) now use these tokens. Built-in +icons render with `fill="currentColor"` and inherit the surrounding +`color`. + +### Deprecated CSS variables + +Two groups of legacy color variables remain available for backward +compatibility, but **both are deprecated and will be removed in a future +major version**. Migrate everything to the `--color--*` semantic tokens. + +**Structural legacy vars** — defined inside `` (e.g. +`--border-color`, `--base-body-color`, `--light-bg-color`, `--alert-color`, +`--add-color`, `--remove-color`, …). Each one now resolves to the closest +semantic token first, falling back to its original light-mode value if the +token is unavailable. So a v3 plugin that still uses them will follow the +active theme. + +**Theme-derived legacy vars** — `--accent-color`, `--primary-color`, +`--light-color`, `--dark-color`, `--semi-transparent-accent-color` (plus +their `*-rgb-components` counterparts). These are emitted from the +deprecated `ctx.theme` field, which the host now pins to **light values +only**, regardless of the active theme. Mixing these with the new +`--color--*` tokens in dark mode will produce a light accent on a dark +surface — visibly mismatched. Replace them with the corresponding +semantic tokens (`--color--accent--surface`, `--color--accent--ink`, +`--color--primary--surface`, etc.). + +Non-color tokens (`--spacing-*`, `--font-size-*`, `--font-weight-bold`, +`--material-ease`, font families) are stable and remain available. diff --git a/packages/react-ui/__tests__/index.test.ts b/packages/react-ui/__tests__/index.test.ts index abe192d..d255f20 100644 --- a/packages/react-ui/__tests__/index.test.ts +++ b/packages/react-ui/__tests__/index.test.ts @@ -1,5 +1,93 @@ -describe('connect()', () => { - it('works', () => { - expect(true).toBeTruthy(); +import { generateStyleFromCtx } from '../src/generateStyleFromCtx'; + +const baseCtx = { + bodyPadding: [10, 20, 10, 20] as [number, number, number, number], + theme: { + primaryColor: 'rgb(0, 76, 209)', + accentColor: 'rgb(0, 76, 209)', + semiTransparentAccentColor: 'rgb(0, 76, 209)', + lightColor: 'rgb(219, 234, 254)', + darkColor: 'rgb(0, 33, 90)', + }, + semanticColorTokensTheme: {}, +}; + +describe('generateStyleFromCtx', () => { + it('generates existing theme CSS variables with RGB components', () => { + const style = generateStyleFromCtx(baseCtx as any) as any; + + expect(style['--primary-color']).toBe('rgb(0, 76, 209)'); + expect(style['--primary-color-rgb-components']).toBe('0, 76, 209'); + expect(style['--accent-color']).toBe('rgb(0, 76, 209)'); + expect(style['--semi-transparent-accent-color']).toBe('rgb(0, 76, 209)'); + expect(style.padding).toBe('10px 20px 10px 20px'); + }); + + it('respects noBodyPadding flag', () => { + const style = generateStyleFromCtx(baseCtx as any, true) as any; + + expect(style.padding).toBeUndefined(); + expect(style['--primary-color']).toBe('rgb(0, 76, 209)'); + }); + + it('applies semantic color tokens verbatim, keyed by their CSS variable name', () => { + const ctx = { + ...baseCtx, + semanticColorTokensTheme: { + '--color--surface': 'rgb(255, 255, 255)', + '--color--ink': 'rgb(52, 54, 58)', + '--color--feedback-fail--ink': 'rgb(255, 94, 73)', + '--shadow--raised': '0 1px 2px rgba(0, 0, 0, 0.1)', + }, + }; + + const style = generateStyleFromCtx(ctx as any) as any; + + expect(style['--color--surface']).toBe('rgb(255, 255, 255)'); + expect(style['--color--ink']).toBe('rgb(52, 54, 58)'); + expect(style['--color--feedback-fail--ink']).toBe('rgb(255, 94, 73)'); + expect(style['--shadow--raised']).toBe('0 1px 2px rgba(0, 0, 0, 0.1)'); + + // Semantic tokens are passed through as-is — no RGB-component derivation. + expect(style['--color--surface-rgb-components']).toBeUndefined(); + expect(style['--color--ink-rgb-components']).toBeUndefined(); + }); + + it('works when semanticColorTokensTheme is undefined', () => { + const { semanticColorTokensTheme, ...ctx } = baseCtx; + const style = generateStyleFromCtx(ctx as any) as any; + + expect(style['--primary-color']).toBe('rgb(0, 76, 209)'); + // Should not throw, no semantic token variables present + expect(style['--color--surface']).toBeUndefined(); + }); + + it('works when semanticColorTokensTheme is an empty object', () => { + const ctx = { + ...baseCtx, + semanticColorTokensTheme: {}, + }; + + const style = generateStyleFromCtx(ctx as any) as any; + + expect(style['--primary-color']).toBe('rgb(0, 76, 209)'); + const semanticKeys = Object.keys(style).filter((k: string) => + k.startsWith('--color--'), + ); + expect(semanticKeys).toHaveLength(0); + }); + + it('forwards arbitrary host tokens the SDK has never heard of', () => { + const ctx = { + ...baseCtx, + semanticColorTokensTheme: { + '--color--brand-new--surface': 'rgb(100, 200, 50)', + }, + }; + + const style = generateStyleFromCtx(ctx as any) as any; + + // The SDK keeps no token list: whatever the host sends is applied as-is. + expect(style['--color--brand-new--surface']).toBe('rgb(100, 200, 50)'); }); }); diff --git a/packages/react-ui/package-lock.json b/packages/react-ui/package-lock.json index f8af25f..3d62a96 100644 --- a/packages/react-ui/package-lock.json +++ b/packages/react-ui/package-lock.json @@ -1,17 +1,17 @@ { "name": "datocms-react-ui", - "version": "2.1.5", + "version": "2.2.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "datocms-react-ui", - "version": "2.1.5", + "version": "2.2.0-alpha.1", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.16", "classnames": "^2.3.1", - "datocms-plugin-sdk": "^2.1.5", + "datocms-plugin-sdk": "^2.2.0-alpha.1", "react-intersection-observer": "^8.31.0", "react-select": "^5.2.1", "scroll-into-view-if-needed": "^2.2.20" diff --git a/packages/react-ui/package.json b/packages/react-ui/package.json index c079f0b..c736413 100644 --- a/packages/react-ui/package.json +++ b/packages/react-ui/package.json @@ -1,6 +1,6 @@ { "name": "datocms-react-ui", - "version": "2.1.5", + "version": "2.2.0-alpha.1", "description": "React components to use inside DatoCMS plugins", "keywords": [ "datocms", @@ -42,7 +42,7 @@ "dependencies": { "@floating-ui/react": "^0.27.16", "classnames": "^2.3.1", - "datocms-plugin-sdk": "^2.1.5", + "datocms-plugin-sdk": "^2.2.0-alpha.1", "react-intersection-observer": "^8.31.0", "react-select": "^5.2.1", "scroll-into-view-if-needed": "^2.2.20" @@ -58,5 +58,5 @@ "postcss-nested": "^5.0.6", "typedoc": "^0.26.7" }, - "gitHead": "a523642c7188862c8431027e51ab479945dde5dd" + "gitHead": "6b88a998f0807c7da1e17afa6a8f0336aed7497f" } diff --git a/packages/react-ui/src/Button/styles.module.css b/packages/react-ui/src/Button/styles.module.css index 6c236fb..97c12f0 100644 --- a/packages/react-ui/src/Button/styles.module.css +++ b/packages/react-ui/src/Button/styles.module.css @@ -7,7 +7,7 @@ cursor: pointer; line-height: inherit; background-color: transparent; - color: var(--base-body-color); + color: var(--color--ink); -webkit-appearance: none; -moz-appearance: none; border-radius: 4px; @@ -32,56 +32,65 @@ } .buttonType-muted { - background-color: var(--light-color); - color: var(--accent-color); + background-color: var(--color--tinted--surface); + color: var(--color--tinted--ink); &.disabled { - background-color: var(--light-bg-color); - color: rgba(0, 0, 0, 0.2); + background-color: var(--color--disabled--surface); + color: var(--color--disabled--ink); &:hover, &:focus, &:active { - color: rgba(0, 0, 0, 0.2); + color: var(--color--disabled--ink); } } } .buttonType-primary { - background-color: var(--accent-color); - color: white; + background-color: var(--color--primary--surface); + color: var(--color--primary--ink); &:hover, &:focus, &:active { - color: white; + color: var(--color--primary--ink); + } + + &:hover { + background-color: var(--color--primary--surface-hover); + } + + &:active, + &:focus { + background-color: var(--color--primary--surface-active); } &.disabled { - background-color: var(--disabled-bg-color); - color: rgba(0, 0, 0, 0.2); + background-color: var(--color--disabled--surface); + color: var(--color--disabled--ink); &:hover, &:focus, &:active { - color: rgba(0, 0, 0, 0.2); + color: var(--color--disabled--ink); } } } .buttonType-negative { - background-color: var(--alert-color); - color: white; + background-color: var(--color--danger--surface); + color: var(--color--danger--ink); &:hover, &:focus, &:active { - color: white; - background-color: var(--alert-color); + color: var(--color--danger--ink); + background-color: var(--color--danger--surface); } &.disabled { - background-color: var(--disabled-bg-color); - color: rgba(0, 0, 0, 0.2); + background-color: var(--color--disabled--surface); + color: var(--color--disabled--ink); } } @@ -128,7 +137,7 @@ line-height: 0.6; svg { - fill: var(--accent-color); + fill: var(--color--ink-accent); } } diff --git a/packages/react-ui/src/ButtonGroup/Button/styles.module.css b/packages/react-ui/src/ButtonGroup/Button/styles.module.css index 5ec7555..dd0c88f 100644 --- a/packages/react-ui/src/ButtonGroup/Button/styles.module.css +++ b/packages/react-ui/src/ButtonGroup/Button/styles.module.css @@ -2,8 +2,8 @@ font-family: inherit; cursor: pointer; line-height: inherit; - background-color: white; - color: var(--base-body-color); + background-color: var(--color--surface); + color: var(--color--ink); -webkit-appearance: none; -moz-appearance: none; font-size: inherit; @@ -11,22 +11,20 @@ border: 0; padding: 0; padding: 7px 13px; - border-right: 1px solid var(--border-color); - border: 1px solid var(--border-color); - border-left-width: 0; + border: 1px solid var(--color--border); cursor: pointer; display: flex; align-items: center; justify-column: center; - color: rgba(var(--base-body-color-rgb-components, 0.6)); + color: var(--color--ink-subtle); } .Button:hover { - background-color: var(--light-bg-color); + background-color: var(--color--surface-hover); } .Button svg { - fill: var(--light-body-color); + fill: var(--color--ink-subtle); } .Button--s { @@ -35,31 +33,29 @@ .Button--disabled { cursor: not-allowed; - color: var(--light-body-color); + color: var(--color--disabled--ink); } .Button--disabled:hover { - background: white; + background: var(--color--surface); } .Button--selected { - background-color: var(--accent-color); - border-color: var(--accent-color); - color: white; + background-color: var(--color--accent--surface); + color: var(--color--accent--ink); } .Button--selected svg { - fill: white; + fill: var(--color--accent--ink); } .Button--selected:hover { - background-color: var(--accent-color); + background-color: var(--color--accent--surface); } .Button--selected:hover, .Button--selected.Button--disabled { - background-color: rgba(var(--accent-color-rgb-components), 0.8); - border-color: rgba(var(--accent-color-rgb-components), 0.8); + background-color: var(--color--accent--surface); } .Button:first-child { diff --git a/packages/react-ui/src/ButtonGroup/Group/styles.module.css b/packages/react-ui/src/ButtonGroup/Group/styles.module.css index 038352a..3c45190 100644 --- a/packages/react-ui/src/ButtonGroup/Group/styles.module.css +++ b/packages/react-ui/src/ButtonGroup/Group/styles.module.css @@ -1,6 +1,6 @@ .Group { display: flex; align-items: stretch; - background-color: white; + background-color: var(--color--surface); overflow: hidden; } diff --git a/packages/react-ui/src/Canvas/index.tsx b/packages/react-ui/src/Canvas/index.tsx index 1c64ecf..a7da49f 100644 --- a/packages/react-ui/src/Canvas/index.tsx +++ b/packages/react-ui/src/Canvas/index.tsx @@ -34,283 +34,186 @@ export type CanvasProps = { }; /** - * @example Color palette CSS variables + * @example Semantic color token CSS variables * - * Within the `Canvas` component, a color palette is made available as a set of - * CSS variables, allowing you to conform to the theme of the current - * environment: + * Inside `Canvas`, the host exposes a full semantic color palette as CSS + * custom properties. Components should reference these tokens directly — + * they adapt to the user's active theme (including dark mode) + * automatically. + * + * ### How to read a token name + * + * ``` + * --color--{property} // standalone (one -- after color) + * --color--{context}--{property} // context pair (two -- after color) + * ``` + * + * **Properties** — `surface` (backgrounds), `ink` (text/icons), + * `border` (1px lines), `outline` (focus rings), plus `fill` / `track` + * for progress bars. + * + * **Standalone** tokens work on any neutral page. **Contexts** are + * self-contained environments: always pair a `surface` with the `ink`, + * `border`, and hover states from the *same* context. Never mix — e.g. + * don't put `--color--primary--ink` on `--color--danger--surface`. + * + * Non-color tokens `--shadow--raised` / `--shadow--floating` / + * `--shadow--lifted` / `--shadow--ambient` are ready-made `box-shadow` + * composites. * * ```js * - *
- * - * - * - * - * + *
+ *
- * --base-body-color - * - *
- *
+ * {[ + * ['--color--surface', 'Default page background'], + * ['--color--surface-hover', 'Hovered row / list item'], + * ['--color--surface-muted', 'Muted section / card background'], + * ['--color--ink', 'Primary text'], + * ['--color--ink-subtle', 'Secondary text / captions'], + * ['--color--ink-hover', 'Text under hover'], + * ['--color--ink-muted', 'De-emphasized text'], + * ['--color--ink-placeholder', 'Input placeholder text'], + * ['--color--ink-primary', 'Brand-highlighted text / icons'], + * ['--color--ink-accent', 'Links / accent text'], + * ['--color--ink-disabled', 'Disabled text'], + * ['--color--border', 'Default 1px border'], + * ['--color--border-hover', 'Border under hover'], + * ].map(([t, d]) => ( + * + * + * + * * - * - * - * - * - * - * - * - * - * - *
{t}{d}
- * --light-body-color - * - *
- *
- * --placeholder-body-color - * - *
- *
+ * ))} + * *
- *
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
- * --light-bg-color - * - *
- *
- * --lighter-bg-color - * - *
- *
- * --disabled-bg-color - * - *
- *
- * --border-color - * - *
- *
- * --darker-border-color - * - *
- *
- * --alert-color - * - *
- *
- * --warning-color - * - *
- *
- * --notice-color - * - *
- *
- * --warning-bg-color - * - *
- *
- * --add-color - * - *
- *
- * --remove-color - * - *
- *
+ * + *
+ * + * {['--color--raised--surface', '--color--raised--surface-hover', '--color--raised--surface-active'] + * .map((t) => ())} + *
{t}
*
- *
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
- * --accent-color - * - *
- *
- * --primary-color - * - *
- *
- * --light-color - * - *
- *
- * --dark-color - * - *
- *
+ * + *
+ * + * {['--color--primary--surface', '--color--primary--surface-hover', '--color--primary--surface-active', '--color--primary--surface-muted', '--color--primary--ink', '--color--primary--border'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--tinted--surface', '--color--tinted--surface-hover', '--color--tinted--surface-active', '--color--tinted--ink', '--color--tinted--border'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--accent--surface', '--color--accent--ink', + * '--color--selected--surface', '--color--selected--ink', '--color--selected--border', + * '--color--disabled--surface', '--color--disabled--ink', + * '--color--danger--surface', '--color--danger--ink'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--focus--border', '--color--focus--outline'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--feedback-fail--ink', '--color--feedback-fail--border', '--color--feedback-fail--outline', '--color--feedback-fail--surface', + * '--color--feedback-warning--ink', '--color--feedback-warning--border', '--color--feedback-warning--outline', '--color--feedback-warning--surface', + * '--color--feedback-success--ink', '--color--feedback-success--border', '--color--feedback-success--outline', '--color--feedback-success--surface'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--highlight--surface'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--diff-added--surface', '--color--diff-added--outline', '--color--diff-added--ink', '--color--diff-added--ink-subtle', + * '--color--diff-removed--surface', '--color--diff-removed--outline', '--color--diff-removed--ink', '--color--diff-removed--ink-subtle', + * '--color--diff-changed--surface', '--color--diff-changed--outline'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--status-draft--ink', '--color--status-outdated--ink', '--color--status-published--ink'] + * .map((t) => ())} + *
{t}Sample text
+ *
+ * + *
+ * + * {['--color--backdrop--surface', '--color--backdrop--ink', + * '--color--overlay--surface', '--color--overlay--surface-hover', '--color--overlay--surface-active', '--color--overlay--ink'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ *

Stacked gives you layered dark surfaces (base → upper) plus action buttons, borders and ink tones. Use it when a dark inline panel needs internal hierarchy.

+ * + * {['--color--stacked--surface', '--color--stacked--surface-upper', + * '--color--stacked--surface-action', '--color--stacked--surface-action-hover', '--color--stacked--surface-action-active', + * '--color--stacked--ink', '--color--stacked--ink-subtle', '--color--stacked--border'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--progress--track', '--color--progress--fill', '--color--progress--fill-hover'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--tooltip--surface', '--color--tooltip--surface-hover', '--color--tooltip--ink', '--color--tooltip--ink-subtle'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--code--surface', '--color--code--ink'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ * + * {['--color--scrollbar--fill'] + * .map((t) => ())} + *
{t}
+ *
+ * + *
+ *
+ * {['--shadow--raised', '--shadow--floating', '--shadow--lifted', '--shadow--ambient'].map((t) => ( + *
+ *
+ * {t} + *
+ * ))} + *
*
* ; * ``` @@ -443,7 +346,7 @@ export type CanvasProps = { * *
*
*
*
*
*
* { diff --git a/packages/react-ui/src/Dropdown/styles.module.css b/packages/react-ui/src/Dropdown/styles.module.css index b2fe907..9b27bc4 100644 --- a/packages/react-ui/src/Dropdown/styles.module.css +++ b/packages/react-ui/src/Dropdown/styles.module.css @@ -10,35 +10,37 @@ .Dropdown__menu__search { padding: 7px; - border-bottom: 1px solid var(--border-color); + border-bottom: 1px solid var(--color--border); } .Dropdown__menu__search__input { display: block; box-sizing: border-box; width: 100%; - border: 1px solid var(--border-color); + border: 1px solid var(--color--border); appearance: none; + background-color: var(--color--surface); background-image: none; transition: border 0.2s var(--material-ease); resize: none; font-family: inherit; + color: inherit; padding: 8px; border-radius: 3px; font-size: 0.9em; &::placeholder { - color: var(--placeholder-body-color); + color: var(--color--ink-placeholder); } &:hover { - border-color: var(--darker-border-color); + border-color: var(--color--border-hover); } &:focus { outline: 0; - border-color: var(--accent-color); - box-shadow: 0 0 0 3px var(--semi-transparent-accent-color); + border-color: var(--color--focus--border); + box-shadow: 0 0 0 3px var(--color--focus--outline); } } @@ -49,8 +51,8 @@ .Dropdown__menu { min-width: 200px; - background-color: white; - box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); + background-color: var(--color--raised--surface); + box-shadow: var(--shadow--floating); border-radius: 4px; margin-bottom: var(--spacing-xl); padding: 1px 0; @@ -64,8 +66,8 @@ .Dropdown__menu__group__title { padding: 5px 15px 3px; - color: var(--light-body-color); - background-color: var(--light-bg-color); + color: var(--color--ink-subtle); + background-color: var(--color--surface-muted); text-transform: uppercase; font-size: var(--font-size-xs); } @@ -77,7 +79,7 @@ .Dropdown__menu__text { text-align: left; padding: 4px 15px; - color: var(--light-body-color); + color: var(--color--ink-subtle); position: relative; display: block; line-height: 1.2; @@ -86,7 +88,7 @@ .Dropdown__menu__option { text-align: left; padding: 4px 15px; - color: var(--base-body-color); + color: var(--color--ink); position: relative; text-decoration: none; white-space: nowrap; @@ -95,7 +97,7 @@ &:hover, &:focus { - background-color: var(--light-bg-color); + background-color: var(--color--surface-hover); } & > a { @@ -106,7 +108,7 @@ } .Dropdown__menu__option--is-selected { - background-color: var(--light-bg-color); + background-color: var(--color--selected--surface); } .Dropdown__menu__option--is-disabled { @@ -118,19 +120,19 @@ } .Dropdown__menu__option--is-dangerous { - color: var(--alert-color); + color: var(--color--feedback-fail--ink); svg { - fill: var(--alert-color); + fill: var(--color--feedback-fail--ink); } &:hover, &:focus { - background-color: var(--alert-color); - color: white; + background-color: var(--color--danger--surface); + color: var(--color--danger--ink); svg { - fill: white; + fill: var(--color--danger--ink); } } } @@ -161,7 +163,7 @@ border-radius: 4px; height: 8px; width: 8px; - background-color: var(--alert-color); + background-color: var(--color--danger--surface); } } @@ -186,8 +188,8 @@ padding-right: 8px; display: inline-block; vertical-align: middle; - color: var(--light-body-color); - fill: var(--light-body-color); + color: var(--color--ink-subtle); + fill: var(--color--ink-subtle); } } @@ -202,7 +204,7 @@ cursor: pointer; line-height: inherit; background-color: transparent; - color: var(--base-body-color); + color: var(--color--ink); -webkit-appearance: none; -moz-appearance: none; box-sizing: border-box; @@ -213,7 +215,7 @@ width: auto; opacity: 0; line-height: 10px; - color: var(--light-body-color); + color: var(--color--ink-subtle); padding: 3px; font-size: 13px; position: relative; @@ -234,21 +236,21 @@ } svg { - fill: var(--light-body-color); + fill: var(--color--ink-subtle); } } .Dropdown__menu__option__icon--delete { - color: var(--alert-color); + color: var(--color--feedback-fail--ink); svg { - fill: var(--alert-color); + fill: var(--color--feedback-fail--ink); } } .Dropdown__menu__separator { margin: 8px 0; height: 1px; - background-color: var(--border-color); + background-color: var(--color--border); } .Dropdown__menu { diff --git a/packages/react-ui/src/FieldError/styles.module.css b/packages/react-ui/src/FieldError/styles.module.css index 17c0df6..1093951 100644 --- a/packages/react-ui/src/FieldError/styles.module.css +++ b/packages/react-ui/src/FieldError/styles.module.css @@ -1,5 +1,5 @@ .fieldError { - color: var(--alert-color); + color: var(--color--feedback-fail--ink); line-height: 1.2; font-size: var(--font-size-xs); margin-top: var(--spacing-s); diff --git a/packages/react-ui/src/FieldHint/styles.module.css b/packages/react-ui/src/FieldHint/styles.module.css index 2d83927..cbd03c6 100644 --- a/packages/react-ui/src/FieldHint/styles.module.css +++ b/packages/react-ui/src/FieldHint/styles.module.css @@ -1,5 +1,5 @@ .fieldHint { - color: var(--light-body-color); + color: var(--color--ink-subtle); line-height: 1.2; font-size: var(--font-size-xs); margin-top: var(--spacing-s); diff --git a/packages/react-ui/src/FormLabel/styles.module.css b/packages/react-ui/src/FormLabel/styles.module.css index 9c43ed1..5cdab54 100644 --- a/packages/react-ui/src/FormLabel/styles.module.css +++ b/packages/react-ui/src/FormLabel/styles.module.css @@ -1,6 +1,6 @@ .formLabel { display: flex; - color: var(--light-body-color); + color: var(--color--ink-subtle); margin-bottom: var(--spacing-s); align-items: center; @@ -12,7 +12,7 @@ } .formLabel--error { - color: var(--alert-color); + color: var(--color--feedback-fail--ink); } .formLabel__label { diff --git a/packages/react-ui/src/HotKey/styles.module.css b/packages/react-ui/src/HotKey/styles.module.css index 5b088b5..588d9f7 100644 --- a/packages/react-ui/src/HotKey/styles.module.css +++ b/packages/react-ui/src/HotKey/styles.module.css @@ -17,6 +17,6 @@ .hotKeyKey { padding: 5px 8px; - background: var(--light-color); + background: var(--color--tinted--surface); border-radius: 3px; } diff --git a/packages/react-ui/src/Section/styles.module.css b/packages/react-ui/src/Section/styles.module.css index 3b27b41..9dd36dd 100644 --- a/packages/react-ui/src/Section/styles.module.css +++ b/packages/react-ui/src/Section/styles.module.css @@ -9,7 +9,7 @@ right: -30px; bottom: -20px; left: -30px; - box-shadow: 0 0 0 4px var(--accent-color); + box-shadow: 0 0 0 4px var(--color--focus--border); border-radius: 4px; animation: pageContentSectionHighligh 4s 0.25s ease-in-out forwards; pointer-events: none; @@ -30,7 +30,7 @@ left: 0; right: 0; height: 1px; - background-color: var(--border-color); + background-color: var(--color--border); z-index: 1; } } @@ -42,7 +42,7 @@ margin-right: var(--spacing-l); padding-left: var(--spacing-m); padding-right: var(--spacing-m); - background-color: white; + background-color: var(--color--surface); position: relative; z-index: 2; display: inline-flex; @@ -66,7 +66,7 @@ width: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; - border-left: 6px solid var(--base-body-color); + border-left: 6px solid var(--color--ink); left: 14px; top: 50%; margin-top: -6px; @@ -85,14 +85,14 @@ @keyframes pageContentSectionHighligh { 0% { - box-shadow: 0 0 0 4px var(--accent-color), - 0 0 0 4px rgba(var(--accent-color-rgb-components), 0.7); + box-shadow: 0 0 0 4px var(--color--focus--border), + 0 0 0 4px var(--color--focus--outline); } 15% { - box-shadow: 0 0 0 4px var(--accent-color), 0 0 0 80px transparent; + box-shadow: 0 0 0 4px var(--color--focus--border), 0 0 0 80px transparent; } 75% { - box-shadow: 0 0 0 4px var(--accent-color), 0 0 0 80px transparent; + box-shadow: 0 0 0 4px var(--color--focus--border), 0 0 0 80px transparent; } 100% { box-shadow: 0 0 0 4px transparent, 0 0 0 80px transparent; diff --git a/packages/react-ui/src/SelectInput/index.tsx b/packages/react-ui/src/SelectInput/index.tsx index 25dec66..55fff40 100644 --- a/packages/react-ui/src/SelectInput/index.tsx +++ b/packages/react-ui/src/SelectInput/index.tsx @@ -18,15 +18,15 @@ const themeConfig: ThemeConfig = (existing) => ({ borderRadius: 0, colors: { ...existing.colors, - primary25: 'var(--semi-transparent-accent-color)', + primary25: 'var(--color--surface-hover)', // disabled - neutral10: 'var(--border-color)', + neutral10: 'var(--color--border)', // normal - neutral20: 'var(--border-color)', + neutral20: 'var(--color--border)', // focused - primary: 'var(--accent-color)', + primary: 'var(--color--focus--border)', // hover - neutral30: 'var(--darker-border-color)', + neutral30: 'var(--color--border-hover)', }, }); @@ -35,7 +35,7 @@ const useStyles = (isDisabled?: boolean, error?: boolean) => { return { placeholder: (provided) => ({ ...provided, - color: 'var(--placeholder-body-color)', + color: 'var(--color--ink-placeholder)', }), container: (provided) => { return { @@ -55,57 +55,84 @@ const useStyles = (isDisabled?: boolean, error?: boolean) => { if (isFocused) { return { ...result, - borderColor: error ? 'var(--alert-color)' : 'var(--accent-color)', - backgroundColor: isDisabled ? 'var(--disabled-color)' : 'white', + borderColor: error + ? 'var(--color--feedback-fail--border)' + : 'var(--color--focus--border)', + backgroundColor: isDisabled + ? 'var(--color--disabled--surface)' + : 'var(--color--surface)', boxShadow: `0 0 0 3px ${ error - ? 'rgba(var(--alert-color-rgb-components), 0.2)' - : 'var(--semi-transparent-accent-color)' + ? 'var(--color--feedback-fail--outline)' + : 'var(--color--focus--outline)' }`, '&:hover': { - borderColor: error ? 'var(--alert-color)' : 'var(--accent-color)', + borderColor: error + ? 'var(--color--feedback-fail--border)' + : 'var(--color--focus--border)', }, }; } return { ...result, - borderColor: error ? 'var(--alert-color)' : 'var(--border-color)', - backgroundColor: isDisabled ? 'var(--disabled-color)' : 'white', + borderColor: error + ? 'var(--color--feedback-fail--border)' + : 'var(--color--border)', + backgroundColor: isDisabled + ? 'var(--color--disabled--surface)' + : 'var(--color--surface)', '&:hover': { borderColor: error - ? 'var(--alert-color)' - : 'var(--darker-border-color)', + ? 'var(--color--feedback-fail--border)' + : 'var(--color--border-hover)', }, }; }, multiValueRemove: (provided) => ({ ...provided, cursor: 'pointer', + color: 'var(--color--tinted--ink)', + ':hover': { + backgroundColor: 'var(--color--tinted--surface-hover)', + color: 'var(--color--tinted--ink)', + }, }), menu: (provided) => { return { ...provided, zIndex: 1000, minWidth: 250, + backgroundColor: 'var(--color--raised--surface)', + boxShadow: 'var(--shadow--floating)', }; }, - input: (provided) => { - const result = { - ...provided, + singleValue: (provided) => ({ + ...provided, + color: 'var(--color--ink)', + }), + input: (provided) => ({ + ...provided, + color: 'var(--color--ink)', + boxShadow: 'none', + 'input:focus': { boxShadow: 'none', - 'input:focus': { - boxShadow: 'none', - }, - }; - - return result; - }, + }, + }), + option: (provided, { isFocused, isSelected }) => ({ + ...provided, + backgroundColor: isSelected + ? 'var(--color--selected--surface)' + : isFocused + ? 'var(--color--surface-hover)' + : undefined, + color: 'var(--color--ink)', + }), multiValue: (provided) => { return { ...provided, zIndex: 100, - backgroundColor: 'var(--light-color)', + backgroundColor: 'var(--color--tinted--surface)', userSelect: 'none', }; }, @@ -113,6 +140,7 @@ const useStyles = (isDisabled?: boolean, error?: boolean) => { ...provided, fontSize: 'inherit', padding: 3, + color: 'var(--color--tinted--ink)', }), }; }, [isDisabled, error]); diff --git a/packages/react-ui/src/SidebarPanel/index.tsx b/packages/react-ui/src/SidebarPanel/index.tsx index 6a42521..5ee3db3 100644 --- a/packages/react-ui/src/SidebarPanel/index.tsx +++ b/packages/react-ui/src/SidebarPanel/index.tsx @@ -1,33 +1,8 @@ import classNames from 'classnames'; import React, { type ReactNode, useState } from 'react'; +import { CaretDownIcon, CaretUpIcon } from '../icons'; import s from './styles.module.css.json'; -function ChevronDownIcon() { - return ( - - - - ); -} - -function ChevronUpIcon() { - return ( - - - - ); -} - export type SidebarPanelProps = { title?: ReactNode; startOpen?: boolean; @@ -44,7 +19,7 @@ export type SidebarPanelProps = { *
* Content @@ -61,7 +36,7 @@ export type SidebarPanelProps = { * display: 'flex', * justifyContent: 'center', * alignItems: 'center', - * background: 'var(--light-bg-color)', + * background: 'var(--color--surface-muted)', * }} * > * Main content @@ -92,7 +67,7 @@ export function SidebarPanel({ >
{title}
- {open ? : } + {open ? : }
)} diff --git a/packages/react-ui/src/SidebarPanel/styles.module.css b/packages/react-ui/src/SidebarPanel/styles.module.css index 2224d9d..4fd32aa 100644 --- a/packages/react-ui/src/SidebarPanel/styles.module.css +++ b/packages/react-ui/src/SidebarPanel/styles.module.css @@ -1,13 +1,13 @@ .SidebarPanel { - border-bottom: 1px solid var(--border-color); + border-bottom: 1px solid var(--color--border); } .SidebarPanel__header { font-family: inherit; cursor: pointer; line-height: inherit; - background-color: white; - color: var(--base-body-color); + background-color: var(--color--surface); + color: var(--color--ink); -webkit-appearance: none; -moz-appearance: none; font-size: inherit; @@ -18,13 +18,13 @@ width: 100%; display: flex; align-items: center; - background-color: var(--light-bg-color); + background-color: var(--color--surface-muted); user-select: none; } .SidebarPanel__header:hover, .SidebarPanel__header:focus { - background-color: var(--lighter-bg-color); + background-color: var(--color--surface-hover); } .SidebarPanel__header__title { @@ -41,7 +41,6 @@ .SidebarPanel__content { padding: 20px; - background-color: white; } .SidebarPanel__content--no-padding { diff --git a/packages/react-ui/src/Spinner/styles.module.css b/packages/react-ui/src/Spinner/styles.module.css index cb2437c..ddce775 100644 --- a/packages/react-ui/src/Spinner/styles.module.css +++ b/packages/react-ui/src/Spinner/styles.module.css @@ -12,7 +12,7 @@ .Spinner__bar { animation: Spinner__spin 1.2s linear infinite; - background-color: var(--light-body-color); + background-color: var(--color--ink-subtle); position: absolute; width: 40%; height: 14%; diff --git a/packages/react-ui/src/SplitView/SplitViewSash/styles.module.css b/packages/react-ui/src/SplitView/SplitViewSash/styles.module.css index c6ee5d0..e5e930a 100644 --- a/packages/react-ui/src/SplitView/SplitViewSash/styles.module.css +++ b/packages/react-ui/src/SplitView/SplitViewSash/styles.module.css @@ -3,7 +3,7 @@ position: absolute; top: 0; transition: background-color 0.2s 0.15s; - background-color: rgba(var(--light-color-components), 0); + background-color: transparent; width: 100%; z-index: 2; display: flex; @@ -13,7 +13,7 @@ .SplitViewSash:hover, .SplitViewSash--dragging { - background-color: var(--light-color); + background-color: var(--color--tinted--surface); } .SplitViewSash:hover:has(.SplitViewSash__content:hover), @@ -47,14 +47,14 @@ width: 20px; height: 20px; border-radius: 6px; - border: 1px solid var(--border-color); - background: white; + border: 1px solid var(--color--border); + background: var(--color--surface); z-index: 2; display: flex; align-items: center; justify-content: center; font-size: 10px; - color: var(--light-body-color); + color: var(--color--ink-subtle); } .SplitViewSash__content__button svg { @@ -63,6 +63,6 @@ } .SplitViewSash__content:hover .SplitViewSash__content__button { - background: var(--light-bg-color); - color: var(--base-body-color); + background: var(--color--surface-hover); + color: var(--color--ink); } diff --git a/packages/react-ui/src/SwitchField/styles.module.css b/packages/react-ui/src/SwitchField/styles.module.css index 4d2f579..71672f3 100644 --- a/packages/react-ui/src/SwitchField/styles.module.css +++ b/packages/react-ui/src/SwitchField/styles.module.css @@ -16,7 +16,7 @@ -ms-user-select: text; user-select: text; margin-bottom: 0; - color: var(--base-body-color); + color: var(--color--ink); } .switchField__below { diff --git a/packages/react-ui/src/SwitchInput/styles.module.css b/packages/react-ui/src/SwitchInput/styles.module.css index 299d6b3..fe442be 100644 --- a/packages/react-ui/src/SwitchInput/styles.module.css +++ b/packages/react-ui/src/SwitchInput/styles.module.css @@ -1,8 +1,9 @@ .switchInput__inner { - color: #fff; + color: var(--color--primary--ink); font-size: 12px; position: absolute; left: 24px; + transition: left 0.3s cubic-bezier(0.35, 0, 0.25, 1); } .switchInput { @@ -14,8 +15,8 @@ line-height: 20px; vertical-align: middle; border-radius: 20px 20px; - border: 1px solid #ccc; - background-color: #ccc; + border: 1px solid var(--color--border); + background-color: var(--color--ink-muted); cursor: pointer; transition: all 0.3s cubic-bezier(0.35, 0, 0.25, 1); @@ -26,18 +27,20 @@ left: 2px; top: 1px; border-radius: 50% 50%; - background-color: #ffffff; + background-color: var(--color--surface); content: ' '; cursor: pointer; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); + box-shadow: var(--shadow--raised); transform: scale(1); - transition: left 0.3s cubic-bezier(0.35, 0, 0.25, 1); + transition: + left 0.3s cubic-bezier(0.35, 0, 0.25, 1), + transform 0.3s cubic-bezier(0.35, 0, 0.25, 1); animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); animation-duration: 0.3s; animation-name: switchInput__off; } - &:hover, + &:hover:after, &:focus:after { transform: scale(1.1); animation-name: switchInput__on; @@ -45,8 +48,7 @@ } .switchInput__checked { - border: 1px solid var(--accent-color); - background-color: var(--accent-color); + background-color: var(--color--primary--surface); .switchInput__inner { left: 6px; @@ -59,16 +61,16 @@ .switchInput__disabled { cursor: no-drop; - background: #ccc; - border-color: #ccc; + background: var(--color--disabled--surface); + border-color: var(--color--border); &:after { - background: #9e9e9e; + background: var(--color--disabled--ink); animation-name: none; cursor: no-drop; } - &:hover, + &:hover:after, &:focus:after { transform: scale(1); animation-name: none; diff --git a/packages/react-ui/src/TextInput/styles.module.css b/packages/react-ui/src/TextInput/styles.module.css index d2d1166..1554ea1 100644 --- a/packages/react-ui/src/TextInput/styles.module.css +++ b/packages/react-ui/src/TextInput/styles.module.css @@ -3,27 +3,29 @@ box-sizing: border-box; width: 100%; padding: 10px; - border: 1px solid var(--border-color); + border: 1px solid var(--color--border); appearance: none; border-radius: 0; + background-color: var(--color--surface); background-image: none; transition: border 0.2s var(--material-ease); font-size: var(--font-size-m); resize: none; font-family: inherit; + color: inherit; &::placeholder { - color: var(--placeholder-body-color); + color: var(--color--ink-placeholder); } &:hover { - border-color: var(--darker-border-color); + border-color: var(--color--border-hover); } &:focus { outline: 0; - border-color: var(--accent-color); - box-shadow: 0 0 0 3px var(--semi-transparent-accent-color); + border-color: var(--color--focus--border); + box-shadow: 0 0 0 3px var(--color--focus--outline); } } @@ -33,20 +35,20 @@ } .TextInput--disabled { - color: var(--light-body-color); - border-color: var(--border-color); - background: var(--lighter-bg-color); + color: var(--color--disabled--ink); + border-color: var(--color--border); + background: var(--color--disabled--surface); } .TextInput--error { - border-color: var(--alert-color); + border-color: var(--color--feedback-fail--border); &:hover, &:focus { - border-color: var(--alert-color); + border-color: var(--color--feedback-fail--border); } &:focus { - box-shadow: 0 0 0 3px rgba(var(--alert-color-rgb-components), 0.2); + box-shadow: 0 0 0 3px var(--color--feedback-fail--outline); } } diff --git a/packages/react-ui/src/TextareaInput/styles.module.css b/packages/react-ui/src/TextareaInput/styles.module.css index 268e4a7..1984bcd 100644 --- a/packages/react-ui/src/TextareaInput/styles.module.css +++ b/packages/react-ui/src/TextareaInput/styles.module.css @@ -3,27 +3,29 @@ box-sizing: border-box; width: 100%; padding: 10px; - border: 1px solid var(--border-color); + border: 1px solid var(--color--border); appearance: none; border-radius: 0; + background-color: var(--color--surface); background-image: none; transition: border 0.2s var(--material-ease); font-size: var(--font-size-m); resize: none; font-family: inherit; + color: inherit; &::placeholder { - color: var(--placeholder-body-color); + color: var(--color--ink-placeholder); } &:hover { - border-color: var(--darker-border-color); + border-color: var(--color--border-hover); } &:focus { outline: 0; - border-color: var(--accent-color); - box-shadow: 0 0 0 3px var(--semi-transparent-accent-color); + border-color: var(--color--focus--border); + box-shadow: 0 0 0 3px var(--color--focus--outline); } } @@ -33,20 +35,20 @@ } .TextareaInput--disabled { - color: var(--light-body-color); - border-color: var(--border-color); - background: var(--lighter-bg-color); + color: var(--color--disabled--ink); + border-color: var(--color--border); + background: var(--color--disabled--surface); } .TextareaInput--error { - border-color: var(--alert-color); + border-color: var(--color--feedback-fail--border); &:hover, &:focus { - border-color: var(--alert-color); + border-color: var(--color--feedback-fail--border); } &:focus { - box-shadow: 0 0 0 3px rgba(var(--alert-color-rgb-components), 0.2); + box-shadow: 0 0 0 3px var(--color--feedback-fail--outline); } } diff --git a/packages/react-ui/src/Toolbar/Button/styles.module.css b/packages/react-ui/src/Toolbar/Button/styles.module.css index 2208c4a..4d7c186 100644 --- a/packages/react-ui/src/Toolbar/Button/styles.module.css +++ b/packages/react-ui/src/Toolbar/Button/styles.module.css @@ -3,7 +3,7 @@ cursor: pointer; line-height: inherit; background-color: transparent; - color: var(--base-body-color); + color: var(--color--ink); -webkit-appearance: none; -moz-appearance: none; box-sizing: border-box; @@ -14,13 +14,13 @@ justify-content: center; width: 49px; min-height: 49px; - border-left: 1px solid var(--border-color); - border-right: 1px solid var(--border-color); + border-left: 1px solid var(--color--border); + border-right: 1px solid var(--color--border); } .Button:hover, .Button:focus { - background-color: var(--light-bg-color); + background-color: var(--color--surface-hover); } .Button:first-child { diff --git a/packages/react-ui/src/Toolbar/Toolbar/index.tsx b/packages/react-ui/src/Toolbar/Toolbar/index.tsx index 92c8423..8349ef3 100644 --- a/packages/react-ui/src/Toolbar/Toolbar/index.tsx +++ b/packages/react-ui/src/Toolbar/Toolbar/index.tsx @@ -23,7 +23,7 @@ export type ToolbarProps = { * display: 'flex', * justifyContent: 'center', * alignItems: 'center', - * background: 'var(--light-bg-color)', + * background: 'var(--color--surface-muted)', * height: '150px', * }} * > @@ -54,7 +54,7 @@ export type ToolbarProps = { * display: 'flex', * justifyContent: 'center', * alignItems: 'center', - * background: 'var(--light-bg-color)', + * background: 'var(--color--surface-muted)', * height: '150px', * }} * > @@ -83,7 +83,7 @@ export type ToolbarProps = { * display: 'flex', * justifyContent: 'center', * alignItems: 'center', - * background: 'var(--light-bg-color)', + * background: 'var(--color--surface-muted)', * height: '150px', * }} * > diff --git a/packages/react-ui/src/Toolbar/Toolbar/styles.module.css b/packages/react-ui/src/Toolbar/Toolbar/styles.module.css index c535e4e..ed753ad 100644 --- a/packages/react-ui/src/Toolbar/Toolbar/styles.module.css +++ b/packages/react-ui/src/Toolbar/Toolbar/styles.module.css @@ -1,7 +1,7 @@ .Toolbar { display: flex; - border-bottom: 1px solid var(--border-color); - border-top: 1px solid var(--border-color); + border-bottom: 1px solid var(--color--border); + border-top: 1px solid var(--color--border); align-items: stretch; position: relative; } diff --git a/packages/react-ui/src/Tooltip/TooltipContent/styles.module.css b/packages/react-ui/src/Tooltip/TooltipContent/styles.module.css index 9d41b47..735baee 100644 --- a/packages/react-ui/src/Tooltip/TooltipContent/styles.module.css +++ b/packages/react-ui/src/Tooltip/TooltipContent/styles.module.css @@ -1,7 +1,7 @@ .tooltip { padding: 10px 15px; - box-shadow: 0 1px 9px rgba(0, 0, 0, 0.2); - background: white; + box-shadow: var(--shadow--raised); + background: var(--color--raised--surface); border-radius: 4px; max-width: 400px; word-wrap: break-word; diff --git a/packages/react-ui/src/Tooltip/TooltipDelayGroup/index.tsx b/packages/react-ui/src/Tooltip/TooltipDelayGroup/index.tsx index 9a7a8d7..b79052a 100644 --- a/packages/react-ui/src/Tooltip/TooltipDelayGroup/index.tsx +++ b/packages/react-ui/src/Tooltip/TooltipDelayGroup/index.tsx @@ -87,8 +87,8 @@ export type TooltipDelayGroupProps = { * display: 'flex', * gap: 'var(--spacing-xs)', * padding: 'var(--spacing-s)', - * borderRadius: 'var(--border-radius-m)', - * backgroundColor: 'var(--light-bg-color)' + * borderRadius: '4px', + * backgroundColor: 'var(--color--surface-muted)' * }}> * * diff --git a/packages/react-ui/src/VerticalSplit/index.tsx b/packages/react-ui/src/VerticalSplit/index.tsx index fbdef5c..fbab80a 100644 --- a/packages/react-ui/src/VerticalSplit/index.tsx +++ b/packages/react-ui/src/VerticalSplit/index.tsx @@ -61,7 +61,7 @@ function calculateSizes({ * Main content *
*
- *
+ *
* * * Secondary @@ -108,7 +108,7 @@ function calculateSizes({ * Sidebar *
*
- *
+ *
* * * Primary @@ -168,7 +168,7 @@ function calculateSizes({ * display: 'flex', * flexDirection: 'column', * height: '100%', - * borderLeft: '1px solid var(--border-color)', + * borderLeft: '1px solid var(--color--border)', * }} * > * @@ -233,7 +233,7 @@ function calculateSizes({ * display: 'flex', * flexDirection: 'column', * height: '100%', - * borderLeft: '1px solid var(--border-color)', + * borderLeft: '1px solid var(--color--border)', * }} * > * diff --git a/packages/react-ui/src/VerticalSplit/styles.module.css b/packages/react-ui/src/VerticalSplit/styles.module.css index 3595512..1c4368f 100644 --- a/packages/react-ui/src/VerticalSplit/styles.module.css +++ b/packages/react-ui/src/VerticalSplit/styles.module.css @@ -1,6 +1,6 @@ .VerticalSplitPane__expand { transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1); - background: white; + background: var(--color--surface); cursor: pointer; position: absolute; top: 0; @@ -10,12 +10,12 @@ } .VerticalSplitPane__expand:hover { - background: var(--light-bg-color); + background: var(--color--surface-hover); animation: VerticalSplitPane__expand 0.6s cubic-bezier(0.55, 0, 0.1, 1); } .VerticalSplitPane__expand.VerticalSplitPane__expand { - border-right: 1px solid var(--border-color); + border-right: 1px solid var(--color--border); transform-origin: left; } @@ -23,7 +23,7 @@ } .VerticalSplitPane__expand.VerticalSplitPane__expand--right { - border-left: 1px solid var(--border-color); + border-left: 1px solid var(--color--border); transform-origin: right; } @@ -45,11 +45,8 @@ left: 0; right: 0; bottom: 0; - background: linear-gradient( - to bottom, - rgba(48, 48, 47, 0.5), - rgba(48, 48, 47, 0.3) - ); + background: var(--color--backdrop--surface); + backdrop-filter: blur(4px); z-index: 12; height: 100%; overflow: hidden; @@ -76,8 +73,8 @@ position: absolute; top: 0; bottom: 0; - background: white; - box-shadow: 0 0 15px rgba(0, 0, 0, 0.4); + background: var(--color--surface); + box-shadow: var(--shadow--ambient); } .VerticalSplitPaneOverlay__secondary--left { diff --git a/packages/react-ui/src/generateStyleFromCtx/index.ts b/packages/react-ui/src/generateStyleFromCtx/index.ts index c9991ed..31e80ec 100644 --- a/packages/react-ui/src/generateStyleFromCtx/index.ts +++ b/packages/react-ui/src/generateStyleFromCtx/index.ts @@ -1,5 +1,5 @@ import type { CSSProperties } from 'react'; -import { BaseCtx } from '../Canvas'; +import type { BaseCtx } from '../Canvas'; function camelToDash(str: string) { if (str === str.toLowerCase()) { @@ -16,6 +16,7 @@ export function generateStyleFromCtx( padding: noBodyPadding ? undefined : ctx.bodyPadding.map((p) => `${p}px`).join(' '), + // Legacy, deprecated color tokens ...Object.fromEntries( Object.entries(ctx.theme).flatMap(([k, v]) => [ [`--${camelToDash(k)}`, v], @@ -25,5 +26,10 @@ export function generateStyleFromCtx( ], ]), ), + // Semantic color tokens arrive keyed by their final CSS custom property + // name (e.g. `--color--raised--surface`), so they're applied verbatim. The + // SDK stays agnostic of the token vocabulary: whatever the host sends ends + // up on the canvas, and plugin CSS references it with `var(--…)`. + ...ctx.semanticColorTokensTheme, }; } diff --git a/packages/react-ui/src/icons.tsx b/packages/react-ui/src/icons.tsx index 9a7bd14..7536ca8 100644 --- a/packages/react-ui/src/icons.tsx +++ b/packages/react-ui/src/icons.tsx @@ -16,6 +16,7 @@ export function BackIcon({ return ( ` mapping CSS + custom property names to their resolved values (e.g. + `{ '--color--surface': '…', '--color--primary--surface': '…' }`). The host + computes these for the user's **active theme**, light or dark per the + user's preference. The SDK applies whatever the host sends verbatim and + keeps no token list of its own, so the vocabulary can evolve host-side + without an SDK release. If you use `datocms-react-ui`, `Canvas` injects + these onto the canvas for you; otherwise apply them yourself, e.g. + `Object.assign(document.documentElement.style, ctx.semanticColorTokensTheme)`. +- `ctx.theme` — still present and unchanged in shape, but **deprecated**. + The host now pins this field to **light-mode values only**, regardless + of the user's active theme, so existing plugins that read it (or use + the `--accent-color` / `--light-color` / `--primary-color` / + `--dark-color` / `--semi-transparent-accent-color` CSS vars derived + from it) keep rendering exactly as they did before. +- `ctx.colorScheme` — `'light'` or `'dark'`. The host has already resolved + `'system'` for you. The SDK runtime also writes + `document.documentElement.dataset.theme` to `"light"` / `"dark"` on + initial handshake and on every ctx update, so you can theme with CSS + selectors like: + + ```css + [data-theme="dark"] .my-panel { background: #222; } + ``` + + For non-CSS decisions (picking a logo asset, a syntax-highlighting + preset, …) branch on `ctx.colorScheme` directly. + +> **Host contract.** `ctx.theme` always returns light-mode colors; +> `ctx.semanticColorTokensTheme` is theme-aware. This lets plugins upgrade +> to the latest version without breaking, and opt into the active theme by +> reading the new field. + +### Action required + +If your plugin uses `datocms-react-ui`, see +[its upgrade notes](../react-ui/UPGRADING.md) — most of the visible +changes are there, including the dark-mode audit checklist. + +If your plugin reads `ctx.theme` directly, you can keep doing so for now. +Migrating to `ctx.semanticColorTokensTheme` will let your plugin follow +the user's active theme (including dark mode). diff --git a/packages/sdk/manifest.json b/packages/sdk/manifest.json index dbd35e5..0a1b520 100644 --- a/packages/sdk/manifest.json +++ b/packages/sdk/manifest.json @@ -3129,13 +3129,34 @@ }, "theme": { "comment": { - "markdownText": "An object containing the theme colors for the current DatoCMS project." + "markdownText": "An object containing the theme colors for the current DatoCMS project.", + "deprecatedMarkdownText": "Use `semanticColorTokensTheme` instead. This property is kept\nfor backward compatibility with third-party plugins." }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 106 + "lineNumber": 111 }, "type": "Theme" + }, + "semanticColorTokensTheme": { + "comment": { + "markdownText": "Semantic color tokens for the current DatoCMS project, pre-computed by\nthe host. A map of CSS custom property names (e.g.\n`--color--raised--surface`) to their resolved values for the current\ncolor scheme." + }, + "location": { + "filePath": "src/ctx/base.ts", + "lineNumber": 119 + }, + "type": "SemanticColorTokensTheme" + }, + "colorScheme": { + "comment": { + "markdownText": "The appearance color scheme the host CMS is currently using. Resolved —\n`'system'` is already expanded to `'light'` or `'dark'` by the host.\n\nThe SDK runtime reflects this onto `document.documentElement` as\n`data-theme=\"light\"` / `data-theme=\"dark\"` so plugin CSS can branch\nwith `[data-theme=\"dark\"] { … }` selectors. For non-CSS decisions\n(choosing a logo asset, a syntax-highlighting preset, …) branch on\n`ctx.colorScheme` directly." + }, + "location": { + "filePath": "src/ctx/base.ts", + "lineNumber": 131 + }, + "type": "'light' | 'dark'" } } }, @@ -3151,7 +3172,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 116 + "lineNumber": 141 }, "type": "Partial>" }, @@ -3161,7 +3182,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 123 + "lineNumber": 148 }, "type": "Partial>" }, @@ -3171,7 +3192,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 130 + "lineNumber": 155 }, "type": "Partial>" }, @@ -3181,7 +3202,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 137 + "lineNumber": 162 }, "type": "Partial>" }, @@ -3191,7 +3212,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 144 + "lineNumber": 169 }, "type": "Partial>" } @@ -3212,7 +3233,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 187 + "lineNumber": 232 }, "type": "(itemTypeId: string) => Promise" }, @@ -3223,7 +3244,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 206 + "lineNumber": 251 }, "type": "(itemTypeId: string) => Promise" }, @@ -3234,7 +3255,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 223 + "lineNumber": 268 }, "type": "() => Promise" }, @@ -3245,7 +3266,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 236 + "lineNumber": 281 }, "type": "() => Promise" }, @@ -3256,7 +3277,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 249 + "lineNumber": 294 }, "type": "() => Promise" } @@ -3275,7 +3296,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 271 + "lineNumber": 316 }, "type": "(params: Record) => Promise" }, @@ -3286,7 +3307,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 322 + "lineNumber": 367 }, "type": "(\n fieldId: string,\n changes: FieldAppearanceChange[],\n ) => Promise" } @@ -3305,7 +3326,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 427 + "lineNumber": 472 }, "type": "(message: string) => Promise" }, @@ -3316,7 +3337,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 442 + "lineNumber": 487 }, "type": "(message: string) => Promise" }, @@ -3327,7 +3348,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 466 + "lineNumber": 511 }, "type": "(\n toast: Toast,\n ) => Promise" } @@ -3346,7 +3367,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 352 + "lineNumber": 397 }, "type": "(itemTypeId: string) => Promise" }, @@ -3357,7 +3378,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 373 + "lineNumber": 418 }, "type": "{\n (\n itemTypeId: string,\n options: { multiple: true; initialLocationQuery?: ItemListLocationQuery },\n ): Promise;\n (\n itemTypeId: string,\n options?: {\n multiple: false;\n initialLocationQuery?: ItemListLocationQuery;\n },\n ): Promise;\n }" }, @@ -3368,7 +3389,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 405 + "lineNumber": 450 }, "type": "(itemId: string) => Promise" } @@ -3387,7 +3408,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 493 + "lineNumber": 538 }, "type": "{\n (options: { multiple: true }): Promise;\n (options?: { multiple: false }): Promise;\n }" }, @@ -3398,7 +3419,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 521 + "lineNumber": 566 }, "type": "(\n uploadId: string,\n ) => Promise<(Upload & { deleted?: true }) | null>" }, @@ -3409,7 +3430,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 549 + "lineNumber": 594 }, "type": "(\n /** The \"single asset\" field structure */\n fileFieldValue: FileFieldValue,\n /** Shows metadata information for a specific locale */\n locale?: string,\n ) => Promise" } @@ -3428,7 +3449,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 580 + "lineNumber": 625 }, "type": "(modal: Modal) => Promise" }, @@ -3439,7 +3460,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 617 + "lineNumber": 662 }, "type": "(options: ConfirmOptions) => Promise" } @@ -3458,7 +3479,7 @@ }, "location": { "filePath": "src/ctx/base.ts", - "lineNumber": 631 + "lineNumber": 676 }, "type": "(path: string) => Promise" } diff --git a/packages/sdk/package-lock.json b/packages/sdk/package-lock.json index 7235d3f..152dd65 100644 --- a/packages/sdk/package-lock.json +++ b/packages/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "datocms-plugin-sdk", - "version": "2.1.5", + "version": "2.2.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "datocms-plugin-sdk", - "version": "2.1.5", + "version": "2.2.0-alpha.1", "license": "MIT", "dependencies": { "@datocms/cma-client": "*", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index dfa3a6e..18a5979 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "datocms-plugin-sdk", - "version": "2.1.5", + "version": "2.2.0-alpha.1", "description": "DatoCMS Plugin SDK", "keywords": [ "datocms", @@ -46,5 +46,5 @@ "glob": "^11.0.0", "typescript": "^5.6.2" }, - "gitHead": "a523642c7188862c8431027e51ab479945dde5dd" + "gitHead": "6b88a998f0807c7da1e17afa6a8f0336aed7497f" } diff --git a/packages/sdk/src/connect.ts b/packages/sdk/src/connect.ts index 4e1ccd3..f669446 100644 --- a/packages/sdk/src/connect.ts +++ b/packages/sdk/src/connect.ts @@ -136,6 +136,15 @@ export type FullConnectParameters = AssetSourcesHook & UploadSidebarsHook & ValidateManualFieldExtensionParametersHook; +function applyColorScheme(properties: unknown): void { + if (typeof document === 'undefined') return; + const next = (properties as { colorScheme?: 'light' | 'dark' } | null) + ?.colorScheme; + if (next !== 'light' && next !== 'dark') return; + if (document.documentElement.dataset.theme === next) return; + document.documentElement.dataset.theme = next; +} + export async function connect( rawConfiguration: Partial = {}, ): Promise { @@ -185,6 +194,7 @@ export async function connect( ), ), onChange(newSettings: unknown) { + applyColorScheme(newSettings); if (onChangeListener) { onChangeListener(newSettings); } @@ -212,6 +222,7 @@ export async function connect( const methods = await penpalConnection.promise; const initialProperties = await methods.getSettings(); + applyColorScheme(initialProperties); if (initialProperties.mode === 'onBoot') { let currentProperties = initialProperties; diff --git a/packages/sdk/src/ctx/base.ts b/packages/sdk/src/ctx/base.ts index 5a5d17a..9b5a891 100644 --- a/packages/sdk/src/ctx/base.ts +++ b/packages/sdk/src/ctx/base.ts @@ -102,8 +102,33 @@ type ProjectProperties = { locale: string; }; - /** An object containing the theme colors for the current DatoCMS project */ + /** + * An object containing the theme colors for the current DatoCMS project + * + * @deprecated Use `semanticColorTokensTheme` instead. This property is kept + * for backward compatibility with third-party plugins. + */ theme: Theme; + + /** + * Semantic color tokens for the current DatoCMS project, pre-computed by + * the host. A map of CSS custom property names (e.g. + * `--color--raised--surface`) to their resolved values for the current + * color scheme. + */ + semanticColorTokensTheme: SemanticColorTokensTheme; + + /** + * The appearance color scheme the host CMS is currently using. Resolved — + * `'system'` is already expanded to `'light'` or `'dark'` by the host. + * + * The SDK runtime reflects this onto `document.documentElement` as + * `data-theme="light"` / `data-theme="dark"` so plugin CSS can branch + * with `[data-theme="dark"] { … }` selectors. For non-CSS decisions + * (choosing a logo asset, a syntax-highlighting preset, …) branch on + * `ctx.colorScheme` directly. + */ + colorScheme: 'light' | 'dark'; }; /** @@ -144,7 +169,12 @@ type EntityReposProperties = { ssoUsers: Partial>; }; -/** An object containing the theme colors for the current DatoCMS project */ +/** + * An object containing the theme colors for the current DatoCMS project + * + * @deprecated Use `SemanticColorTokensTheme` instead. This type is kept for + * backward compatibility with third-party plugins. + */ export type Theme = { primaryColor: string; accentColor: string; @@ -153,6 +183,21 @@ export type Theme = { darkColor: string; }; +/** + * Semantic color tokens for the current DatoCMS project, pre-computed by the + * host. Only available on DatoCMS hosts that support the new token system. + * + * Each key is a ready-to-use CSS custom property name (including the leading + * `--`), and each value is the resolved color for the host's current color + * scheme — e.g. `{ '--color--raised--surface': 'oklch(…)' }`. The SDK applies + * these verbatim onto the plugin canvas, so plugin CSS can reference them + * directly with `var(--color--raised--surface)`. + * + * The token set is whatever the host sends; it is intentionally untyped so it + * can evolve on the host without an SDK release. + */ +export type SemanticColorTokensTheme = Record; + export type BaseMethods = LoadDataMethods & UpdatePluginParametersMethods & ToastMethods & diff --git a/packages/sdk/src/manifest.ts b/packages/sdk/src/manifest.ts index e752c27..aeb1bb2 100644 --- a/packages/sdk/src/manifest.ts +++ b/packages/sdk/src/manifest.ts @@ -3362,13 +3362,37 @@ export const manifest: Manifest = { comment: { markdownText: 'An object containing the theme colors for the current DatoCMS project.', + deprecatedMarkdownText: + 'Use `semanticColorTokensTheme` instead. This property is kept\nfor backward compatibility with third-party plugins.', }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 106, + lineNumber: 111, }, type: 'Theme', }, + semanticColorTokensTheme: { + comment: { + markdownText: + 'Semantic color tokens for the current DatoCMS project, pre-computed by\nthe host. A map of CSS custom property names (e.g.\n`--color--raised--surface`) to their resolved values for the current\ncolor scheme.', + }, + location: { + filePath: 'src/ctx/base.ts', + lineNumber: 119, + }, + type: 'SemanticColorTokensTheme', + }, + colorScheme: { + comment: { + markdownText: + 'The appearance color scheme the host CMS is currently using. Resolved —\n`\'system\'` is already expanded to `\'light\'` or `\'dark\'` by the host.\n\nThe SDK runtime reflects this onto `document.documentElement` as\n`data-theme="light"` / `data-theme="dark"` so plugin CSS can branch\nwith `[data-theme="dark"] { … }` selectors. For non-CSS decisions\n(choosing a logo asset, a syntax-highlighting preset, …) branch on\n`ctx.colorScheme` directly.', + }, + location: { + filePath: 'src/ctx/base.ts', + lineNumber: 131, + }, + type: "'light' | 'dark'", + }, }, }, { @@ -3385,7 +3409,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 116, + lineNumber: 141, }, type: 'Partial>', }, @@ -3396,7 +3420,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 123, + lineNumber: 148, }, type: 'Partial>', }, @@ -3407,7 +3431,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 130, + lineNumber: 155, }, type: 'Partial>', }, @@ -3418,7 +3442,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 137, + lineNumber: 162, }, type: 'Partial>', }, @@ -3429,7 +3453,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 144, + lineNumber: 169, }, type: 'Partial>', }, @@ -3453,7 +3477,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 187, + lineNumber: 232, }, type: '(itemTypeId: string) => Promise', }, @@ -3466,7 +3490,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 206, + lineNumber: 251, }, type: '(itemTypeId: string) => Promise', }, @@ -3479,7 +3503,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 223, + lineNumber: 268, }, type: '() => Promise', }, @@ -3492,7 +3516,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 236, + lineNumber: 281, }, type: '() => Promise', }, @@ -3505,7 +3529,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 249, + lineNumber: 294, }, type: '() => Promise', }, @@ -3527,7 +3551,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 271, + lineNumber: 316, }, type: '(params: Record) => Promise', }, @@ -3540,7 +3564,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 322, + lineNumber: 367, }, type: '(\n fieldId: string,\n changes: FieldAppearanceChange[],\n ) => Promise', }, @@ -3562,7 +3586,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 427, + lineNumber: 472, }, type: '(message: string) => Promise', }, @@ -3575,7 +3599,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 442, + lineNumber: 487, }, type: '(message: string) => Promise', }, @@ -3588,7 +3612,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 466, + lineNumber: 511, }, type: '(\n toast: Toast,\n ) => Promise', }, @@ -3610,7 +3634,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 352, + lineNumber: 397, }, type: '(itemTypeId: string) => Promise', }, @@ -3623,7 +3647,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 373, + lineNumber: 418, }, type: '{\n (\n itemTypeId: string,\n options: { multiple: true; initialLocationQuery?: ItemListLocationQuery },\n ): Promise;\n (\n itemTypeId: string,\n options?: {\n multiple: false;\n initialLocationQuery?: ItemListLocationQuery;\n },\n ): Promise;\n }', }, @@ -3636,7 +3660,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 405, + lineNumber: 450, }, type: '(itemId: string) => Promise', }, @@ -3658,7 +3682,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 493, + lineNumber: 538, }, type: '{\n (options: { multiple: true }): Promise;\n (options?: { multiple: false }): Promise;\n }', }, @@ -3671,7 +3695,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 521, + lineNumber: 566, }, type: '(\n uploadId: string,\n ) => Promise<(Upload & { deleted?: true }) | null>', }, @@ -3684,7 +3708,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 549, + lineNumber: 594, }, type: '(\n /** The "single asset" field structure */\n fileFieldValue: FileFieldValue,\n /** Shows metadata information for a specific locale */\n locale?: string,\n ) => Promise', }, @@ -3706,7 +3730,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 580, + lineNumber: 625, }, type: '(modal: Modal) => Promise', }, @@ -3719,7 +3743,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 617, + lineNumber: 662, }, type: '(options: ConfirmOptions) => Promise', }, @@ -3740,7 +3764,7 @@ export const manifest: Manifest = { }, location: { filePath: 'src/ctx/base.ts', - lineNumber: 631, + lineNumber: 676, }, type: '(path: string) => Promise', },