Skip to content

Commit

Permalink
chore: add SD demo implementing the spec
Browse files Browse the repository at this point in the history
  • Loading branch information
jorenbroekema committed Nov 7, 2024
1 parent e50e27c commit d0826e1
Show file tree
Hide file tree
Showing 19 changed files with 2,625 additions and 34 deletions.
29 changes: 29 additions & 0 deletions docs/src/components/sd-demo/resolver.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "Demo resolver",
"description": "This demonstrates the resolver spec",
"sets": [
{
"name": "core",
"values": ["core.json"]
},
{
"name": "modes",
"values": ["light.json", "dark.json"]
}
],
"modifiers": [
{
"name": "mode",
"values": [
{
"name": "light",
"values": ["core", "light.json"]
},
{
"name": "dark",
"values": ["core", "dark.json"]
}
]
}
]
}
235 changes: 235 additions & 0 deletions docs/src/components/sd-demo/sd-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import StyleDictionary from 'style-dictionary';
import { fs } from 'style-dictionary/fs';
import type { TransformedToken } from 'style-dictionary/types';
import { permutateThemes } from '@tokens-studio/sd-transforms';
import type { ThemeObject } from '@tokens-studio/types';
import { LitElement, css, html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { init } from '../../monaco/monaco.js';
import core from './tokens/core.json';
import light from './tokens/light.json';
import dark from './tokens/dark.json';
import resolver from './resolver.json';

const files = {
core,
light,
dark,
resolver,
};

async function mirrorTokenFiles() {
await fs.promises.mkdir('tokens');
const write = async (path: string, content: object) => {
return fs.promises.writeFile(path, JSON.stringify(content, null, 2));
};
await Promise.all(
(
[
['tokens/core.json', files.core],
['tokens/light.json', files.light],
['tokens/dark.json', files.dark],
] as [string, object][]
).map(([path, content]) => write(path, content)),
);
}

export class SdDemo extends LitElement {
static get styles() {
return [
css`
table {
text-align: left;
font-size: 12px;
margin-bottom: 2rem;
}
@keyframes render {
from {
background: #ccf;
}
to {
background: transparent;
}
}
th {
padding: 0 1em;
animation: 0.5s render;
}
.color-box {
display: inline-block;
margin-right: 0.5em;
vertical-align: sub;
width: 20px;
height: 20px;
}
details {
margin-top: 2em;
}
`,
];
}

static get properties() {
return {
mode: {
state: true,
},
tokensArray: {
state: true,
},
};
}

declare _mode: 'dark' | 'light';
declare tokensArray: TransformedToken[];
declare themesData: Record<string, string[]>;
declare hasInitialized: Promise<void>;
declare editor: any;
declare hasInitializedResolve: () => void;

get mode() {
return this._mode;
}

set mode(v) {
this._mode = v;
this.onThemeChange();
}

constructor() {
super();
this.hasInitialized = new Promise((resolve) => {
this.hasInitializedResolve = resolve;
});
this.themesData = {};
this.tokensArray = [];
this.editor = undefined;
this.mode = 'dark';
this.init();
}

async init() {
await mirrorTokenFiles();
const permutateInput = this.mapToPermutateThemesInput(files.resolver);
this.themesData = permutateThemes(permutateInput);
await this.updateComplete;
const slotEl = this.shadowRoot?.querySelector('slot[name="monaco-editor"]') as HTMLSlotElement;
const editorElem = slotEl.assignedNodes()[0];
this.editor = await init(editorElem as HTMLDivElement);
this.editor.setValue(JSON.stringify(files.resolver, null, 2));
this.hasInitializedResolve();
}

async onThemeChange() {
await this.hasInitialized;
const sd = new StyleDictionary({
source: this.themesData[this.mode].map((themeSets: string) => `tokens/${themeSets}`),
log: {
verbosity: 'verbose',
},
platforms: {
foo: {
transforms: ['name/kebab'],
},
},
});
const { allTokens } = await sd.getPlatformTokens('foo');
this.tokensArray = allTokens;
}

mapToPermutateThemesInput(res: typeof files.resolver) {
let count = 0;
const resolveSetName = (setNameOrPath: string) => {
if (setNameOrPath.includes('.')) {
return [setNameOrPath];
}
return res.sets.find((set) => set.name === setNameOrPath)?.values;
};

return res.modifiers.reduce((acc, curr) => {
const variants = curr.values.map((variant) => ({
id: `${count++}`,
group: curr.name,
name: variant.name,
selectedTokenSets: Object.fromEntries(
variant.values.flatMap(resolveSetName).map((set) => [set, 'enabled']),
),
})) as ThemeObject[];
return [...acc, ...variants];
}, [] as ThemeObject[]);
}

render() {
return html`
<label>
Mode
<select
@change=${(e: Event) =>
(this.mode = (e.target as HTMLSelectElement).value as 'light' | 'dark')}
>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</label>
${this.tokensArray
? html`<table>
<caption>
Tokens Table
</caption>
<thead>
<tr>
<th>Name</th>
<th>Resolved</th>
<th>Original</th>
<th>Type</th>
</tr>
</thead>
<tbody>
${this.tokensArray.map(
(token) => html`
<tr>
<th>${token.name}</th>
${unsafeHTML(`<th>${this.renderValue(token)}</th>`)}
${unsafeHTML(`<th>${token.original.$value}</th>`)}
<th>${token.$type}</th>
${unsafeHTML(`<th>${token.filePath}</th>`)}
</tr>
`,
)}
</tbody>
</table>`
: ``}
<select
@change=${(e: Event) =>
this.switchFile((e.target as HTMLSelectElement).value as keyof typeof files)}
>
<option value="resolver">resolver.json</option>
<option value="core">tokens/core.json</option>
<option value="dark">tokens/dark.json</option>
<option value="light">tokens/light.json</option>
</select>
<slot name="monaco-editor"></slot>
`;
}

switchFile(file: keyof typeof files) {
this.editor.setValue(JSON.stringify(files[file], null, 2));
}

renderValue(token: TransformedToken) {
switch (token.$type) {
case 'color':
return `<div class="color-box" style="background-color: ${token.$value};"></div>${token.$value}`;
default:
return `${token.$value}`;
}
}
}

customElements.define('sd-demo', SdDemo);
69 changes: 69 additions & 0 deletions docs/src/components/sd-demo/tokens/core.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"colors": {
"$type": "color",
"black": {
"$value": "#000000"
},
"white": {
"$value": "#ffffff"
},
"gray": {
"100": {
"$value": "#f7fafc"
},
"200": {
"$value": "#edf2f7"
},
"300": {
"$value": "#e2e8f0"
},
"400": {
"$value": "#cbd5e0"
},
"500": {
"$value": "#a0aec0"
},
"600": {
"$value": "#718096"
},
"700": {
"$value": "#4a5568"
},
"800": {
"$value": "#2d3748"
},
"900": {
"$value": "#1a202c"
}
},
"orange": {
"100": {
"$value": "#fffaf0"
},
"200": {
"$value": "#feebc8"
},
"300": {
"$value": "#fbd38d"
},
"400": {
"$value": "#f6ad55"
},
"500": {
"$value": "#ed8936"
},
"600": {
"$value": "#dd6b20"
},
"700": {
"$value": "#c05621"
},
"800": {
"$value": "#9c4221"
},
"900": {
"$value": "#7b341e"
}
}
}
}
15 changes: 15 additions & 0 deletions docs/src/components/sd-demo/tokens/dark.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"colors": {
"$type": "color",
"background": {
"primary": {
"$value": "{colors.orange.300}"
}
},
"foreground": {
"primary": {
"$value": "{colors.gray.100}"
}
}
}
}
15 changes: 15 additions & 0 deletions docs/src/components/sd-demo/tokens/light.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"colors": {
"$type": "color",
"background": {
"primary": {
"$value": "{colors.orange.700}"
}
},
"foreground": {
"primary": {
"$value": "{colors.gray.900}"
}
}
}
}
Loading

0 comments on commit d0826e1

Please sign in to comment.