diff --git a/package.json b/package.json index f814055..73ed133 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dialog-closedby-polyfill": "^1.1.0", "fractional-indexing": "^3.2.0", "hdr-color-input": "^0.2.5", + "interestfor": "^1.0.7", "invokers-polyfill": "^0.5.7", "json-stringify-pretty-compact": "^4.0.0", "keyux": "^0.11.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ab9839..124666a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: hdr-color-input: specifier: ^0.2.5 version: 0.2.5 + interestfor: + specifier: ^1.0.7 + version: 1.0.7 invokers-polyfill: specifier: ^0.5.7 version: 0.5.7 @@ -579,6 +582,9 @@ packages: hdr-color-input@0.2.5: resolution: {integrity: sha512-/3AUOnWIOxh0qJbnFOFa33LnTGgHxiQgiI1KI7LILus0qcMYw07v11GjXnXfmPfntAhVaGXXjNDDhgmiUJ4vWw==} + interestfor@1.0.7: + resolution: {integrity: sha512-qJaN6JMrAjiA+aQ6TkQemZhLx5E8kGI9s0yXAJJbQHSKs0iBKrDc5nMNgGqH1V4x0ZveFJjVbmText/zTI7ZBg==} + invokers-polyfill@0.5.7: resolution: {integrity: sha512-T1MmQ40+ARzC0W3YgyVUyncXIi1G7jnUq1PVdsYNr1ql7etYGHWNr3RKvJABIiFO5Ktk3xK9/A8+1lFASvcrKw==} @@ -1186,6 +1192,8 @@ snapshots: '@preact/signals-core': 1.12.1 colorjs.io: 0.5.2 + interestfor@1.0.7: {} + invokers-polyfill@0.5.7: {} is-reference@3.0.3: diff --git a/src/alias-token.svelte b/src/alias-token.svelte new file mode 100644 index 0000000..95bdf31 --- /dev/null +++ b/src/alias-token.svelte @@ -0,0 +1,239 @@ + + + + +
+ {#if reference} + {reference.replace(/[{}]/g, "").split(".").join(" > ")} + {:else} + Make an alias for another token + {/if} +
+ +
+
+ + { + aliasSearchInput = event.currentTarget.value; + selectedAliasIndex = 0; + }} + onkeydown={handleAliasKeyDown} + /> + {#if reference} + + {/if} +
+ +
+ + diff --git a/src/app.css b/src/app.css index 3d7ea11..7057e47 100644 --- a/src/app.css +++ b/src/app.css @@ -9,17 +9,31 @@ --bg-secondary: #393939; --bg-tertiary: #383838; --bg-hover: #404040; + --bg-reference: oklch(42% 0.2 300); + --bg-reference-hover: oklch(50% 0.2 300); --border-color: #454545; --text-primary: #e6e6e6; --text-secondary: #999999; --accent: #18a0fb; --accent-hover: #27affe; + --popover-shadow: + 0 2px 5px 0 rgb(0 0 0 / 35%), inset 0 0 0.5px 0 rgb(255 255 255 / 35%), + 0 10px 16px 0 rgb(0 0 0 / 35%), inset 0 0.5px 0 0 rgb(255 255 255 / 8%); --panel-header-height: 40px; font-family: var(--typography-geometric-humanist); accent-color: var(--bg-secondary); } +/* reduce show delay when another tooltip is shown already */ +:root:has([interestfor]:has-interest) [interestfor] { + interest-show-delay: 100ms; +} + +:root:has([interestfor].has-interest) [interestfor] { + --interest-show-delay: 100ms; +} + * { box-sizing: border-box; scrollbar-width: thin; @@ -119,19 +133,26 @@ color-input::part(trigger) { align-items: center; justify-content: center; border: 1px solid transparent; - background: transparent; + background-color: transparent; border-radius: 4px; color: var(--text-secondary); transition: all 0.2s ease; font-family: inherit; font-size: 14px; font-weight: 600; + + &[aria-pressed="true"] { + background-color: var(--bg-reference); + &:hover { + background-color: var(--bg-reference-hover); + } + } } .a-button:hover, /* cannot use css nesting inside of pseudo-element */ color-input::part(trigger):hover { - background: var(--bg-hover); + background-color: var(--bg-hover); color: var(--text-primary); } @@ -166,20 +187,30 @@ color-input::part(trigger):focus-visible { background: var(--bg-primary); border: 0; padding: 0; - box-shadow: - 0 2px 5px 0 rgb(0 0 0 / 35%), - inset 0 0 0.5px 0 rgb(255 255 255 / 35%), - 0 10px 16px 0 rgb(0 0 0 / 35%), - inset 0 0.5px 0 0 rgb(255 255 255 / 8%); + box-shadow: var(--popover-shadow); } .a-menu { - position-area: center bottom; + position-area: bottom; position-try-fallbacks: flip-block; margin-inline: 0; margin-block: 8px; } +.a-tooltip { + width: max-content; + background: var(--bg-secondary); + color: var(--text-primary); + border: 0; + padding: 4px 12px; + margin-inline: 0; + margin-block: 8px; + border-radius: 4px; + box-shadow: var(--popover-shadow); + position-area: center top; + position-try-fallbacks: flip-block; +} + .a-item { display: block; width: 100%; diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..0f70856 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,7 @@ +declare module "svelte/elements" { + export interface DOMAttributes { + interestfor?: string; + } +} + +export {}; diff --git a/src/app.svelte b/src/app.svelte index acf2e4c..c990652 100644 --- a/src/app.svelte +++ b/src/app.svelte @@ -3,6 +3,7 @@ import "invokers-polyfill"; import "dialog-closedby-polyfill"; import "hdr-color-input"; + import "interestfor"; {#snippet dimensionEditor( @@ -511,13 +394,29 @@
- handleNameChange(e.currentTarget.value)} - /> +
+ handleNameChange(e.currentTarget.value)} + /> + {#if node?.meta.nodeType === "token" && tokenValue} + { + /* set resolved value when reference is removed */ + updateMeta({ value: newReference ?? tokenValue.value }); + }} + /> + {/if} +
@@ -582,86 +481,12 @@ {/if}
- {#if meta?.nodeType === "token" && availableTokens.length > 0} -
- -
- { - aliasSearchInput = event.currentTarget.value; - selectedAliasIndex = 0; - aliasPopoverElement?.showPopover(); - }} - onkeydown={handleAliasKeyDown} - onclick={() => aliasPopoverElement?.showPopover()} - onfocus={() => aliasPopoverElement?.showPopover()} - onblur={() => { - // Clear search after a brief delay to allow click handling - setTimeout(() => { - aliasSearchInput = ""; - selectedAliasIndex = 0; - aliasPopoverElement?.hidePopover(); - }, 200); - }} - /> - {#if isAlias} - - {/if} -
- - - -
- {/if} - {#if tokenValue?.type === "color"}
{ // track both open and close because of bug in css-color-component const input = event.target as HTMLInputElement; @@ -685,7 +510,6 @@ class="a-field dimension-value" type="number" value={tokenValue.value.value} - disabled={isAlias} oninput={(e) => { const value = Number.parseFloat(e.currentTarget.value); if (!Number.isNaN(value)) { @@ -699,7 +523,6 @@ id="dimension-unit-input" class="a-field dimension-unit-select" value={tokenValue.value.unit} - disabled={isAlias} onchange={(e) => { updateMeta({ value: { @@ -726,7 +549,6 @@ class="a-field duration-value" type="number" value={tokenValue.value.value} - disabled={isAlias} oninput={(e) => { const value = Number.parseFloat(e.currentTarget.value); if (!Number.isNaN(value)) { @@ -740,7 +562,6 @@ id="duration-unit-input" class="a-field duration-unit-select" value={tokenValue.value.unit} - disabled={isAlias} onchange={(e) => { updateMeta({ value: { @@ -765,7 +586,6 @@ class="a-field" type="number" value={tokenValue.value} - disabled={isAlias} oninput={(e) => { const value = Number.parseFloat(e.currentTarget.value); if (!Number.isNaN(value)) { @@ -781,13 +601,9 @@
- {@render fontFamilyEditor( - tokenValue.value, - (value) => { - updateMeta({ value }); - }, - isAlias, - )} + {@render fontFamilyEditor(tokenValue.value, (value) => { + updateMeta({ value }); + })}
{/if} @@ -795,13 +611,9 @@
- {@render fontWeightEditor( - tokenValue.value, - (value) => { - updateMeta({ value }); - }, - isAlias, - )} + {@render fontWeightEditor(tokenValue.value, (value) => { + updateMeta({ value }); + })}
{/if} @@ -811,7 +623,6 @@ { updateMeta({ value }); }} @@ -829,7 +640,6 @@ class="a-field duration-value" type="number" value={tokenValue.value.duration.value} - disabled={isAlias} step="1" placeholder="Value" oninput={(e) => { @@ -850,7 +660,6 @@ { updateMeta({ value: { @@ -923,7 +730,6 @@ { updateMeta({ value: { ...tokenValue.value, timingFunction: value }, @@ -937,79 +743,151 @@
- {@render fontFamilyEditor( - tokenValue.value.fontFamily, - (fontFamily) => { - updateMeta({ - value: { ...tokenValue.value, fontFamily }, - }); - }, - isAlias, - )} + {@render fontFamilyEditor(tokenValue.value.fontFamily, (fontFamily) => { + updateMeta({ + value: { ...tokenValue.value, fontFamily }, + }); + })}
- {@render dimensionEditor( - tokenValue.value.fontSize, - (fontSize) => { +
+ {@render dimensionEditor(tokenValue.value.fontSize, (fontSize) => { updateMeta({ value: { ...tokenValue.value, fontSize }, }); - }, - isAlias, - )} + })} + {#if node?.meta?.nodeType === "token" && !isTokenReference(node.meta.value)} + { + updateMeta({ + value: { + ...(node.meta as any).value, + fontSize: newReference ?? tokenValue.value.fontSize, + }, + }); + }} + /> + {/if} +
- {@render fontWeightEditor( - tokenValue.value.fontWeight, - (fontWeight) => { - updateMeta({ - value: { ...tokenValue.value, fontWeight }, - }); - }, - isAlias, - )} +
+ {@render fontWeightEditor( + tokenValue.value.fontWeight, + (fontWeight) => { + updateMeta({ + value: { ...tokenValue.value, fontWeight }, + }); + }, + )} + {#if node?.meta?.nodeType === "token" && !isTokenReference(node.meta.value)} + { + updateMeta({ + value: { + ...(node.meta as any).value, + fontWeight: newReference ?? tokenValue.value.fontWeight, + }, + }); + }} + /> + {/if} +
- { - const value = Number.parseFloat(e.currentTarget.value); - if (!Number.isNaN(value)) { - updateMeta({ - value: { ...tokenValue.value, lineHeight: value }, - }); - } - }} - step="0.1" - placeholder="e.g., 1.5" - /> +
+ { + const value = Number.parseFloat(e.currentTarget.value); + if (!Number.isNaN(value)) { + updateMeta({ + value: { ...tokenValue.value, lineHeight: value }, + }); + } + }} + step="0.1" + placeholder="e.g., 1.5" + /> + {#if node?.meta?.nodeType === "token" && !isTokenReference(node.meta.value)} + { + updateMeta({ + value: { + ...(node.meta as any).value, + lineHeight: newReference ?? tokenValue.value.lineHeight, + }, + }); + }} + /> + {/if} +
- {@render dimensionEditor( - tokenValue.value.letterSpacing, - (letterSpacing) => { - updateMeta({ - value: { ...tokenValue.value, letterSpacing }, - }); - }, - isAlias, - )} +
+ {@render dimensionEditor( + tokenValue.value.letterSpacing, + (letterSpacing) => { + updateMeta({ + value: { ...tokenValue.value, letterSpacing }, + }); + }, + )} + {#if node?.meta?.nodeType === "token" && !isTokenReference(node.meta.value)} + { + updateMeta({ + value: { + ...(node.meta as any).value, + letterSpacing: + newReference ?? tokenValue.value.letterSpacing, + }, + }); + }} + /> + {/if} +
{/if} @@ -1018,13 +896,9 @@
- {@render strokeStyleEditor( - tokenValue.value, - (value) => { - updateMeta({ value }); - }, - isAlias, - )} + {@render strokeStyleEditor(tokenValue.value, (value) => { + updateMeta({ value }); + })}
{/if} @@ -1042,7 +916,6 @@ class="a-checkbox" type="checkbox" checked={item.inset ?? false} - disabled={isAlias} onchange={(e) => { const updated = [...shadows]; updated[index].inset = e.currentTarget.checked || undefined; @@ -1058,7 +931,6 @@
{/each}