poveste 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @deprecated
2
+ * @deprecated Use {@link logEvent} instead. This forwards to it and warns.
3
3
  */
4
4
  export function hstEvent(name: string, argument): void
5
5
 
@@ -15,5 +15,13 @@ export function logEvent(name: string, argument): void
15
15
  */
16
16
  export function isCollecting(): boolean
17
17
 
18
+ /**
19
+ * Switches the book between light and dark, or to `value` when one is given.
20
+ * Returns the mode it ended up in.
21
+ */
18
22
  export function toggleDark(value?: boolean): boolean
23
+
24
+ /**
25
+ * Whether the book is currently in dark mode.
26
+ */
19
27
  export function isDark(): boolean
@@ -0,0 +1,26 @@
1
+ import type { ServerStoryFile } from '@poveste/shared';
2
+ export interface BuildCounts {
3
+ /** Story files that produced a story with variants. */
4
+ stories: number;
5
+ variants: number;
6
+ /** Standalone `.story.md` pages, which the book's home page calls Documents. */
7
+ docs: number;
8
+ /** Relative paths of the files that produced neither, so the warning can name them. */
9
+ empty: string[];
10
+ }
11
+ /**
12
+ * Splits the collected story files three ways, the way the book's own home page
13
+ * does: stories, documents, and files that produced nothing.
14
+ *
15
+ * "No variants" is not the question. A standalone `.story.md` is collected into a
16
+ * story with `docsOnly: true` and `variants: []` by construction (`markdown.ts`),
17
+ * so counting empty by variants alone reported every documentation page in the
18
+ * book as a problem — three of the four `examples/vue3` warned about are
19
+ * `SHARED_STORY_TITLES` entries that a required spec fails if you delete them,
20
+ * which left the one real empty file hidden inside them (#670).
21
+ *
22
+ * Documents are counted rather than folded in with stories because `HomeView.vue`
23
+ * already makes exactly this split, and a build that reports 60 stories over a
24
+ * home page reading 57 Stories and 3 Documents disagrees with itself.
25
+ */
26
+ export declare function buildCounts(storyFiles: ServerStoryFile[]): BuildCounts;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Splits the collected story files three ways, the way the book's own home page
3
+ * does: stories, documents, and files that produced nothing.
4
+ *
5
+ * "No variants" is not the question. A standalone `.story.md` is collected into a
6
+ * story with `docsOnly: true` and `variants: []` by construction (`markdown.ts`),
7
+ * so counting empty by variants alone reported every documentation page in the
8
+ * book as a problem — three of the four `examples/vue3` warned about are
9
+ * `SHARED_STORY_TITLES` entries that a required spec fails if you delete them,
10
+ * which left the one real empty file hidden inside them (#670).
11
+ *
12
+ * Documents are counted rather than folded in with stories because `HomeView.vue`
13
+ * already makes exactly this split, and a build that reports 60 stories over a
14
+ * home page reading 57 Stories and 3 Documents disagrees with itself.
15
+ */
16
+ export function buildCounts(storyFiles) {
17
+ const counts = { stories: 0, variants: 0, docs: 0, empty: [] };
18
+ for (const file of storyFiles) {
19
+ if (file.story?.docsOnly) {
20
+ counts.docs++;
21
+ }
22
+ else if (file.story?.variants.length) {
23
+ counts.stories++;
24
+ counts.variants += file.story.variants.length;
25
+ }
26
+ else {
27
+ counts.empty.push(file.relativePath);
28
+ }
29
+ }
30
+ return counts;
31
+ }
@@ -5,6 +5,7 @@ import { join } from 'pathe';
5
5
  import pc from 'picocolors';
6
6
  import { createServer as createViteServer, mergeConfig as mergeViteConfig, build as viteBuild, } from 'vite';
7
7
  import { APP_PATH } from './alias.js';
8
+ import { buildCounts } from './build-counts.js';
8
9
  import { getSerializedStoryData } from './build-serialize.js';
9
10
  import { useCollectStories } from './collect/index.js';
10
11
  import { useModuleLoader } from './load.js';
@@ -105,9 +106,7 @@ export async function build(ctx) {
105
106
  // Same wrapper the dev path uses for these exact calls (server.ts).
106
107
  await wrapLogError('destroyCollectStories', () => destroyCollectStories());
107
108
  }
108
- const storyCount = ctx.storyFiles.reduce((sum, file) => sum + (file.story?.variants.length ? 1 : 0), 0);
109
- const variantCount = ctx.storyFiles.reduce((sum, file) => sum + (file.story?.variants.length ?? 0), 0);
110
- const emptyStoryCount = ctx.storyFiles.length - storyCount;
109
+ const { stories: storyCount, variants: variantCount, docs: docsCount, empty } = buildCounts(ctx.storyFiles);
111
110
  const { viteConfig: buildViteConfigRaw } = await getViteConfigWithPlugins(false, ctx);
112
111
  const buildViteConfig = mergeViteConfig(buildViteConfigRaw, {
113
112
  // Vite picks the `.env` file from `mode`, not from NODE_ENV — this said
@@ -261,10 +260,11 @@ export async function build(ctx) {
261
260
  await writeFile('__sandbox.html', sandboxHtml, ctx);
262
261
  await writeFile('poveste.json', JSON.stringify(getSerializedStoryData(ctx), null, 2), ctx);
263
262
  const duration = performance.now() - startTime;
264
- if (emptyStoryCount) {
265
- console.warn(pc.yellow(`⚠️ ${emptyStoryCount} empty story file${emptyStoryCount === 1 ? '' : 's'}`));
263
+ if (empty.length) {
264
+ console.warn(pc.yellow(`⚠️ ${empty.length} empty story file${empty.length === 1 ? '' : 's'}: ${empty.join(', ')}`));
266
265
  }
267
- console.log(pc.green(`✅ Built ${storyCount} stor${storyCount === 1 ? 'y' : 'ies'} (${variantCount} variant${variantCount === 1 ? '' : 's'}) in ${Math.round(duration / 1000 * 100) / 100}s`));
266
+ const docsNote = docsCount ? ` and ${docsCount} document${docsCount === 1 ? '' : 's'}` : '';
267
+ console.log(pc.green(`✅ Built ${storyCount} stor${storyCount === 1 ? 'y' : 'ies'} (${variantCount} variant${variantCount === 1 ? '' : 's'})${docsNote} in ${Math.round(duration / 1000 * 100) / 100}s`));
268
268
  // Render
269
269
  if (previewStoryCallbacks.length) {
270
270
  const { baseUrl, close } = await startPreview({}, ctx);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "poveste",
3
3
  "type": "module",
4
- "version": "0.13.1",
4
+ "version": "0.14.0",
5
5
  "description": "Interactive component playgrounds for Vue, Nuxt, Svelte, SvelteKit and Quasar — a drop-in fork of histoire, with Quasar support histoire does not ship.",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan",
@@ -73,10 +73,10 @@
73
73
  },
74
74
  "dependencies": {
75
75
  "@akryum/tinypool": "^0.3.1",
76
- "@poveste/app": "^0.13.1",
77
- "@poveste/controls": "^0.13.1",
78
- "@poveste/shared": "^0.13.1",
79
- "@poveste/vendors": "^0.13.1",
76
+ "@poveste/app": "^0.14.0",
77
+ "@poveste/controls": "^0.14.0",
78
+ "@poveste/shared": "^0.14.0",
79
+ "@poveste/vendors": "^0.14.0",
80
80
  "@types/markdown-it": "^14.1.2",
81
81
  "birpc": "^0.2.19",
82
82
  "change-case": "^5.4.4",
@@ -109,7 +109,7 @@
109
109
  "@types/node": "^22.10.1",
110
110
  "typescript": "5.6.3",
111
111
  "vite": "^8.2.0",
112
- "vitest": "^4.1.10"
112
+ "vitest": "^5.0.0"
113
113
  },
114
114
  "scripts": {
115
115
  "build": "rimraf dist && tsc",
package/plugin.d.ts CHANGED
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Sends a custom event from the book UI to the plugin's `onDevEvent` hook, and
3
+ * resolves with whatever that hook returns.
4
+ *
5
+ * Development only: the API it calls is installed by the dev server, so in a
6
+ * built book this resolves to `undefined` without erroring.
7
+ */
1
8
  export function sendEvent(event: string, payload?: any): Promise<any>
2
9
 
10
+ /**
11
+ * Navigates the book to a story by its `id` — the same field `getStories()`
12
+ * returns on the plugin API. Development only, and a no-op in a built book.
13
+ */
3
14
  export function openStory(storyId: string): void
package/plugin.js CHANGED
@@ -2,6 +2,11 @@ export function sendEvent(event, payload) {
2
2
  if (window.__HST_PLUGIN_API__) {
3
3
  return window.__HST_PLUGIN_API__.sendEvent(event, payload)
4
4
  }
5
+ // The dev server installs that API and a built book has no plugin host, so
6
+ // this is the ordinary path there. Resolve rather than fall off the end: the
7
+ // signature promises a `Promise`, and returning `undefined` meant
8
+ // `sendEvent(...).then(...)` threw in a built book while `await` was fine.
9
+ return Promise.resolve(undefined)
5
10
  }
6
11
 
7
12
  export function openStory(storyId) {
@@ -1 +0,0 @@
1
- export declare const css = ".__pvt-shade {\n height: 80px;\n border-radius: 4px;\n}\n\n.__pvt-text {\n font-size: 4rem;\n display: flex;\n align-items: flex-end;\n}\n\n.__pvt-border {\n border-style: solid;\n border-width: 2px;\n}\n\n.__pvt-spaces {\n background-color: rgb(113 113 122 / 0.1);\n width: min-content;\n}\n\n.__pvt-margin {\n border: dashed 1px rgb(113 113 122 / 0.5);\n width: min-content;\n}\n\n.__pvt-spaces-box,\n.__pvt-margin-box {\n width: 5rem;\n height: 5rem;\n background-color: rgb(113 113 122 / 0.5);\n}\n\n.__pvt-spaces,\n.__pvt-spaces-box,\n.__pvt-margin,\n.__pvt-margin-box,\n.__pvt-drop-shadow {\n border-radius: 4px;\n}\n\n.__pvt-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.__pvt-drop-shadow {\n width: 8rem;\n height: 8rem;\n background: white;\n margin-bottom: 0.5rem;\n}\n\n.__pvt-drop-shadow {\n background: #4e4e57;\n}\n\n.__pvt-border-radius {\n width: 8rem;\n height: 8rem;\n background-color: rgb(113 113 122 / 0.5);\n}\n\n.__pvt-border-width {\n width: 8rem;\n height: 8rem;\n border-color: rgb(113 113 122 / 0.5);\n background-color: rgb(113 113 122 / 0.1);\n}\n\n.__pvt-width {\n background-color: rgb(113 113 122 / 0.1);\n}\n\n.__pvt-width-box,\n.__pvt-height {\n background-color: rgb(113 113 122 / 0.5);\n}\n\n.__pvt-width-box {\n height: 5rem;\n}";
@@ -1,85 +0,0 @@
1
- // Injected as `style.css` next to the generated story file, so it styles
2
- // the Pinceau token stories without reaching the book's own stylesheet.
3
- export const css = `.__pvt-shade {
4
- height: 80px;
5
- border-radius: 4px;
6
- }
7
-
8
- .__pvt-text {
9
- font-size: 4rem;
10
- display: flex;
11
- align-items: flex-end;
12
- }
13
-
14
- .__pvt-border {
15
- border-style: solid;
16
- border-width: 2px;
17
- }
18
-
19
- .__pvt-spaces {
20
- background-color: rgb(113 113 122 / 0.1);
21
- width: min-content;
22
- }
23
-
24
- .__pvt-margin {
25
- border: dashed 1px rgb(113 113 122 / 0.5);
26
- width: min-content;
27
- }
28
-
29
- .__pvt-spaces-box,
30
- .__pvt-margin-box {
31
- width: 5rem;
32
- height: 5rem;
33
- background-color: rgb(113 113 122 / 0.5);
34
- }
35
-
36
- .__pvt-spaces,
37
- .__pvt-spaces-box,
38
- .__pvt-margin,
39
- .__pvt-margin-box,
40
- .__pvt-drop-shadow {
41
- border-radius: 4px;
42
- }
43
-
44
- .__pvt-truncate {
45
- overflow: hidden;
46
- text-overflow: ellipsis;
47
- white-space: nowrap;
48
- }
49
-
50
- .__pvt-drop-shadow {
51
- width: 8rem;
52
- height: 8rem;
53
- background: white;
54
- margin-bottom: 0.5rem;
55
- }
56
-
57
- .__pvt-drop-shadow {
58
- background: #4e4e57;
59
- }
60
-
61
- .__pvt-border-radius {
62
- width: 8rem;
63
- height: 8rem;
64
- background-color: rgb(113 113 122 / 0.5);
65
- }
66
-
67
- .__pvt-border-width {
68
- width: 8rem;
69
- height: 8rem;
70
- border-color: rgb(113 113 122 / 0.5);
71
- background-color: rgb(113 113 122 / 0.1);
72
- }
73
-
74
- .__pvt-width {
75
- background-color: rgb(113 113 122 / 0.1);
76
- }
77
-
78
- .__pvt-width-box,
79
- .__pvt-height {
80
- background-color: rgb(113 113 122 / 0.5);
81
- }
82
-
83
- .__pvt-width-box {
84
- height: 5rem;
85
- }`;
@@ -1,6 +0,0 @@
1
- import type { Plugin } from '@poveste/shared';
2
- export interface PinceauTokensOptions {
3
- configOrPaths?: string | string[];
4
- configFileName?: string;
5
- }
6
- export declare function pinceauTokens(_options?: PinceauTokensOptions): Plugin;
@@ -1,424 +0,0 @@
1
- import { join } from 'pathe';
2
- import { getInjectedImport } from '../util/vendors.js';
3
- import { css } from './pinceau-tokens-style.js';
4
- export function pinceauTokens(_options = {}) {
5
- const themePath = join(process.cwd(), 'node_modules/.vite/pinceau/index.js');
6
- async function generate(api) {
7
- try {
8
- await api.fs.ensureDir(api.pluginTempDir);
9
- await api.fs.emptyDir(api.pluginTempDir);
10
- api.moduleLoader.clearCache();
11
- await api.fs.writeFile(api.path.resolve(api.pluginTempDir, 'style.css'), css);
12
- const theme = await api.moduleLoader.loadModule(themePath);
13
- const storyFile = api.path.resolve(api.pluginTempDir, 'Pinceau.story.js');
14
- await api.fs.writeFile(storyFile, storyTemplate(theme));
15
- api.addStoryFile(storyFile);
16
- }
17
- catch (e) {
18
- api.error(e.stack ?? e.message);
19
- }
20
- }
21
- return {
22
- name: 'builtin:pinceau-tokens',
23
- config(config) {
24
- // Add 'design-system' group
25
- if (!config.tree) {
26
- config.tree = {};
27
- }
28
- if (!config.tree.groups) {
29
- config.tree.groups = [];
30
- }
31
- if (!config.tree.groups.some(g => g.id === 'design-system')) {
32
- let index = 0;
33
- // After 'top' group
34
- const topIndex = config.tree.groups.findIndex(g => g.id === 'top');
35
- if (topIndex > -1) {
36
- index = topIndex + 1;
37
- }
38
- // Insert group
39
- config.tree.groups.splice(index, 0, {
40
- id: 'design-system',
41
- title: 'Design System',
42
- });
43
- }
44
- },
45
- async onDev(api, onCleanup) {
46
- await generate(api);
47
- const watcher = api.watcher.watch(themePath)
48
- .on('change', () => generate(api))
49
- .on('add', () => generate(api));
50
- onCleanup(() => {
51
- watcher.close();
52
- });
53
- },
54
- async onBuild(api) {
55
- await generate(api);
56
- },
57
- };
58
- }
59
- function storyTemplate(pinceauConfig) {
60
- pinceauConfig = pinceauConfig?.theme || pinceauConfig?.default?.theme || {};
61
- // No `import 'poveste-style'` — see the tailwind plugin's template (#219).
62
- return `import './style.css'
63
- import { createApp, h, markRaw, ref } from ${getInjectedImport('@poveste/vendors/vue')}
64
- import {
65
- HstColorShades,
66
- HstTokenList,
67
- HstTokenGrid,
68
- HstText,
69
- HstTextarea,
70
- HstNumber,
71
- } from ${getInjectedImport('@poveste/controls')}
72
-
73
- const config = markRaw(${JSON.stringify(pinceauConfig, null, 2)})
74
- const search = ref('')
75
- const sampleText = ref('Cat sit like bread eat prawns daintily with a claw then lick paws clean wash down prawns with a lap of carnation milk then retire to the warmest spot on the couch to claw at the fabric before taking a catnap mrow cat cat moo moo lick ears lick paws')
76
- const fontSize = ref(16)
77
- const flattenTokens = (data, transformValue = (value) => (value?.value || value)) => {
78
- const output = {}
79
- function step(obj, prev) {
80
- Object.keys(obj).forEach((key) => {
81
- const value = obj[key]
82
- const isarray = Array.isArray(value)
83
- const type = Object.prototype.toString.call(value)
84
- const isobject
85
- = type === '[object Object]'
86
- || type === '[object Array]'
87
-
88
- const newKey = prev ? prev + '.' + key : key
89
-
90
- if (value.value && !output[newKey]) {
91
- output[newKey] = transformValue(value)
92
- }
93
-
94
- if (!isarray && isobject && Object.keys(value).length) { return step(value, newKey) }
95
- })
96
- }
97
- step(data)
98
- return output
99
- }
100
-
101
- function mountApp ({ el, state, onUnmount }, render) {
102
- Object.assign(state, {
103
- search,
104
- sampleText,
105
- fontSize,
106
- })
107
-
108
- const app = createApp({
109
- render,
110
- })
111
- app.mount(el)
112
-
113
- onUnmount(() => {
114
- app.unmount()
115
- })
116
- }
117
-
118
- /*
119
- media?: PinceauTokens
120
- colors?: ScaleTokens
121
- fonts?: PinceauTokens
122
- fontWeights?: FontWeightTokens
123
- fontSizes?: BreakpointsTokens
124
- size?: BreakpointsTokens
125
- space?: BreakpointsTokens
126
- radii?: BreakpointsTokens
127
- borders?: BreakpointsTokens
128
- borderWidths?: BreakpointsTokens
129
- borderStyles?: BreakpointsTokens
130
- shadows?: BreakpointsTokens
131
- opacity?: BreakpointsTokens
132
- leads?: BreakpointsTokens
133
- letterSpacings?: BreakpointsTokens
134
- transitions?: PinceauTokens
135
- zIndices?: PinceauTokens
136
- */
137
-
138
- export default {
139
- id: 'pinceau',
140
- title: 'Pinceau',
141
- group: 'design-system',
142
- icon: 'noto:paintbrush',
143
- responsiveDisabled: true,
144
- layout: { type: 'single', iframe: false },
145
- variants: [
146
- {
147
- id: 'media-queries',
148
- title: 'Media Queries',
149
- icon: 'carbon:screen',
150
- onMount: (api) => mountApp(api, () => h(HstTokenGrid, {
151
- tokens: flattenTokens(config.media),
152
- getName: key => '@mq.' + key,
153
- colSize: 180,
154
- }, ({ token }) => h('div', {
155
- class: '__pvt-drop-shadow',
156
- }))),
157
- },
158
- {
159
- id: 'colors',
160
- title: 'Colors',
161
- icon: 'carbon:color-palette',
162
- onMount: (api) => mountApp(api, () => Object.entries(config.colors).map(([key, shades]) => h(HstColorShades, {
163
- key,
164
- shades: shades.value ? { DEFAULT: shades.value } : flattenTokens(shades),
165
- getName: shade => '{colors.' + key + '.' + shade + '}',
166
- search: search.value,
167
- }, ({ color}) => h('div', {
168
- class: '__pvt-shade',
169
- style: {
170
- backgroundColor: color,
171
- },
172
- })))),
173
- onMountControls: (api) => mountApp(api, () => [
174
- h(HstText, {
175
- title: 'Filter...',
176
- modelValue: search.value,
177
- 'onUpdate:modelValue': value => { search.value = value },
178
- }),
179
- ]),
180
- },
181
- // SPACES
182
- {
183
- id: 'Spaces',
184
- title: 'Spaces',
185
- icon: 'carbon:area',
186
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
187
- tokens: flattenTokens(config.space),
188
- getName: key => '{space.' + key + '}',
189
- }, ({ token }) => h('div', {
190
- class: '__pvt-spaces',
191
- style: {
192
- padding: token.value,
193
- },
194
- }, [
195
- h('div', {
196
- class: '__pvt-spaces-box',
197
- }),
198
- ]))),
199
- },
200
- // SIZES
201
- {
202
- id: 'sizes',
203
- title: 'Sizes',
204
- icon: 'carbon:pan-horizontal',
205
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
206
- tokens: flattenTokens(config.size),
207
- getName: key => '{size.' + key + '}',
208
- }, ({ token }) => h('div', {
209
- class: '__pvt-width',
210
- }, [
211
- h('div', {
212
- class: '__pvt-width-box',
213
- style: {
214
- width: token.value,
215
- },
216
- }),
217
- ]))),
218
- },
219
- // FONTSIZES
220
- {
221
- id: 'font-size',
222
- title: 'Font Size',
223
- icon: 'carbon:text-font',
224
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
225
- tokens: flattenTokens(config.fontSizes),
226
- getName: key => '{fontSizes.' + key + '}',
227
- }, ({ token }) => h('div', {
228
- class: '__pvt-truncate',
229
- style: {
230
- fontSize: token.value,
231
- },
232
- }, sampleText.value))),
233
- onMountControls: (api) => mountApp(api, () => [
234
- h(HstTextarea, {
235
- title: 'Sample text',
236
- modelValue: sampleText.value,
237
- 'onUpdate:modelValue': value => { sampleText.value = value },
238
- rows: 5,
239
- }),
240
- ]),
241
- },
242
- // FONTWEIGHTS
243
- {
244
- id: 'font-weight',
245
- title: 'Font Weight',
246
- icon: 'carbon:text-font',
247
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
248
- tokens: flattenTokens(config.fontWeights),
249
- getName: key => '{fontWeights.' + key + '}',
250
- }, ({ token }) => h('div', {
251
- class: '__pvt-truncate',
252
- style: {
253
- fontWeight: token.value,
254
- fontSize: \`\${fontSize.value}px\`,
255
- },
256
- }, sampleText.value))),
257
- onMountControls: (api) => mountApp(api, () => [
258
- h(HstTextarea, {
259
- title: 'Sample text',
260
- modelValue: sampleText.value,
261
- 'onUpdate:modelValue': value => { sampleText.value = value },
262
- rows: 5,
263
- }),
264
- h(HstNumber, {
265
- title: 'Font size',
266
- modelValue: fontSize.value,
267
- 'onUpdate:modelValue': value => { fontSize.value = value },
268
- min: 1,
269
- }),
270
- ]),
271
- },
272
- // FONTS
273
- {
274
- id: 'fonts',
275
- title: 'Fonts',
276
- icon: 'carbon:text-font',
277
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
278
- tokens: flattenTokens(config.fonts),
279
- getName: key => '{fonts.' + key + '}',
280
- }, ({ token }) => h('div', {
281
- class: '__pvt-truncate',
282
- style: {
283
- fontFamily: token.value,
284
- fontSize: \`\${fontSize.value}px\`,
285
- },
286
- }, sampleText.value))),
287
- onMountControls: (api) => mountApp(api, () => [
288
- h(HstTextarea, {
289
- title: 'Sample text',
290
- modelValue: sampleText.value,
291
- 'onUpdate:modelValue': value => { sampleText.value = value },
292
- rows: 5,
293
- }),
294
- h(HstNumber, {
295
- title: 'Font size',
296
- modelValue: fontSize.value,
297
- 'onUpdate:modelValue': value => { fontSize.value = value },
298
- min: 1,
299
- }),
300
- ]),
301
- },
302
- // LETTERSPACINGS
303
- {
304
- id: 'letter-spacing',
305
- title: 'Letter Spacing',
306
- icon: 'carbon:text-font',
307
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
308
- tokens: flattenTokens(config.letterSpacings),
309
- getName: key => '{letterSpacings.' + key + '}',
310
- }, ({ token }) => h('div', {
311
- class: '__pvt-truncate',
312
- style: {
313
- letterSpacing: token.value,
314
- fontSize: \`\${fontSize.value}px\`,
315
- },
316
- }, sampleText.value))),
317
- onMountControls: (api) => mountApp(api, () => [
318
- h(HstTextarea, {
319
- title: 'Sample text',
320
- modelValue: sampleText.value,
321
- 'onUpdate:modelValue': value => { sampleText.value = value },
322
- rows: 5,
323
- }),
324
- h(HstNumber, {
325
- title: 'Font size',
326
- modelValue: fontSize.value,
327
- 'onUpdate:modelValue': value => { fontSize.value = value },
328
- min: 1,
329
- }),
330
- ]),
331
- },
332
- // LEADS
333
- {
334
- id: 'leads',
335
- title: 'Leads',
336
- icon: 'carbon:text-font',
337
- onMount: (api) => mountApp(api, () => h(HstTokenList, {
338
- tokens: flattenTokens(config.leads),
339
- getName: key => '{leads.' + key + '}',
340
- }, ({ token }) => h('div', {
341
- style: {
342
- lineHeight: token.value,
343
- },
344
- }, sampleText.value))),
345
- onMountControls: (api) => mountApp(api, () => [
346
- h(HstTextarea, {
347
- title: 'Sample text',
348
- modelValue: sampleText.value,
349
- 'onUpdate:modelValue': value => { sampleText.value = value },
350
- rows: 5,
351
- }),
352
- // @TODO select font size
353
- ]),
354
- },
355
- // SHADOWS
356
- {
357
- id: 'shadows',
358
- title: 'Shadows',
359
- icon: 'carbon:shape-except',
360
- onMount: (api) => mountApp(api, () => h(HstTokenGrid, {
361
- tokens: flattenTokens(config.shadows),
362
- getName: key => '{shadows.' + key + '}',
363
- colSize: 180,
364
- }, ({ token }) => h('div', {
365
- class: '__pvt-drop-shadow',
366
- style: 'box-shadow:' + token.value,
367
- }))),
368
- },
369
- // OPACITIES
370
- {
371
- id: 'opacities',
372
- title: 'Opacities',
373
- icon: 'carbon:bring-forward',
374
- onMount: (api) => mountApp(api, () => h(HstTokenGrid, {
375
- tokens: flattenTokens(config.opacity),
376
- getName: key => '{opacity.' + key + '}',
377
- colSize: 180,
378
- }, ({ token }) => h('div', {
379
- class: '__pvt-drop-shadow',
380
- style: 'opacity:' + token.value,
381
- }))),
382
- },
383
- // RADII
384
- {
385
- id: 'border-radius',
386
- title: 'Border Radius',
387
- icon: 'carbon:condition-wait-point',
388
- onMount: (api) => mountApp(api, () => h(HstTokenGrid, {
389
- tokens: flattenTokens(config.radii),
390
- getName: key => '{radii.' + key + '}',
391
- colSize: 180,
392
- }, ({ token }) => h('div', {
393
- class: '__pvt-border-radius',
394
- style: {
395
- borderRadius: token.value,
396
- },
397
- }))),
398
- },
399
- // BORDERWIDTHS
400
- {
401
- id: 'border-width',
402
- title: 'Border Width',
403
- icon: 'carbon:checkbox',
404
- onMount: (api) => mountApp(api, () => h(HstTokenGrid, {
405
- tokens: flattenTokens(config.borderWidths),
406
- getName: key => '{borderWidths.' + key + '}',
407
- colSize: 180,
408
- }, ({ token }) => h('div', {
409
- class: '__pvt-border-width',
410
- style: {
411
- borderWidth: token.value,
412
- },
413
- }))),
414
- },
415
- // FULL CONFIG
416
- {
417
- id: 'full-config',
418
- title: 'Full Config',
419
- icon: 'carbon:code',
420
- onMount: (api) => mountApp(api, () => h('pre', JSON.stringify(config, null, 2))),
421
- },
422
- ],
423
- }`;
424
- }