Skip to content

Commit 8032698

Browse files
committed
fix(json-render-ui): restore shadow-root theming and scrollbars
1 parent 4fcf5dc commit 8032698

6 files changed

Lines changed: 72 additions & 28 deletions

File tree

design/build-shadow-css.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,11 @@ export interface BuildShadowCssOptions {
3030
*/
3131
primaryRampPath: string
3232
/**
33-
* Absolute path to a hand-authored stylesheet run through the generator's
34-
* configured transformers (directives, variant groups) and merged in
35-
* right after the CSS reset. Omit for a package with no hand-written
36-
* styles.
33+
* Absolute paths to hand-authored stylesheets run through the generator's
34+
* configured transformers (directives, variant groups) and merged in order
35+
* right after the CSS reset. Omit for a package with no hand-written styles.
3736
*/
38-
userStylePath?: string
37+
userStylePaths?: string[]
3938
/**
4039
* Prefix Wind's `--un-*` custom properties are renamed to (see
4140
* `namespaceShadowCssVars`) — unique per shadow-root surface so two
@@ -61,7 +60,7 @@ export interface BuildShadowCssResult {
6160
// generated file itself; returns stats so each caller (a `scripts/` entry,
6261
// exempt from the `no-console` lint rule) prints its own summary line.
6362
export async function buildShadowCss(options: BuildShadowCssOptions): Promise<BuildShadowCssResult> {
64-
const { srcDir, globs, config, primaryRampPath, userStylePath, varPrefix } = options
63+
const { srcDir, globs, config, primaryRampPath, userStylePaths, varPrefix } = options
6564
const generatedCss = join(srcDir, '.generated/css.ts')
6665

6766
const require = createRequire(import.meta.url)
@@ -93,15 +92,15 @@ export async function buildShadowCss(options: BuildShadowCssOptions): Promise<Bu
9392
await generator.applyExtractors(content, file, tokens)
9493
}
9594

96-
// The hand-written stylesheet (if any) may use `--at-apply` — run it
97-
// through the configured transformers (directives, variant groups) before
98-
// merging.
99-
const userStyle = userStylePath
100-
? new MagicString(await fs.readFile(userStylePath, 'utf-8').catch(() => ''))
101-
: undefined
102-
if (userStyle) {
95+
// Hand-written stylesheets may use `--at-apply`. Run each through the
96+
// configured transformers before merging them in the caller's order.
97+
const userStyles = await Promise.all((userStylePaths ?? []).map(async userStylePath => ({
98+
path: userStylePath,
99+
source: new MagicString(await fs.readFile(userStylePath, 'utf-8').catch(() => '')),
100+
})))
101+
for (const userStyle of userStyles) {
103102
for (const transformer of generator.config.transformers ?? []) {
104-
await transformer.transform(userStyle, userStylePath!, { uno: generator } as any)
103+
await transformer.transform(userStyle.source, userStyle.path, { uno: generator } as any)
105104
}
106105
}
107106

@@ -126,7 +125,7 @@ export async function buildShadowCss(options: BuildShadowCssOptions): Promise<Bu
126125
// `namespaceShadowCssVars`).
127126
let css = [
128127
reset,
129-
userStyle?.toString(),
128+
...userStyles.map(userStyle => userStyle.source.toString()),
130129
unoCss,
131130
surfacesCss,
132131
primaryRamp,

packages/hub-ui/scripts/build-css.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const { sourceCount, css } = await buildShadowCss({
1717
globs: ['components/**/*.{ts,vue}', 'state/**/*.ts', 'embedded/**/*.ts', 'standalone/**/*.{ts,html}'],
1818
config,
1919
primaryRampPath: join(SRC_DIR, 'primary-ramp.css'),
20-
userStylePath: join(SRC_DIR, 'style.css'),
20+
userStylePaths: [join(SRC_DIR, 'style.css')],
2121
varPrefix: '--un-hub-',
2222
})
2323
console.log(`${c.green('✓')} CSS built (${sourceCount} sources, ${(css.length / 1024).toFixed(1)} kB)`)

packages/json-render-ui/scripts/build-css.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { createRequire } from 'node:module'
12
import { join } from 'node:path'
23
import { fileURLToPath } from 'node:url'
34
import { colors as c } from 'devframe/utils/colors'
@@ -12,12 +13,17 @@ import config from '../uno.config'
1213
// host page. See `design/build-shadow-css.ts` for the shared pipeline
1314
// (mirrored by `@devframes/hub-ui`'s `scripts/build-css.ts`).
1415
const SRC_DIR = join(fileURLToPath(new URL('..', import.meta.url)), 'src')
16+
const moduleRequire = createRequire(import.meta.url)
1517

1618
const { sourceCount, css } = await buildShadowCss({
1719
srcDir: SRC_DIR,
1820
globs: ['components/**/*.ts', 'renderer.ts', 'dock-renderer.ts', 'renderer-module/**/*.ts'],
1921
config,
2022
primaryRampPath: join(SRC_DIR, 'renderer-module/primary-ramp.css'),
23+
userStylePaths: [
24+
moduleRequire.resolve('@antfu/design/styles/scrollbar.css'),
25+
join(SRC_DIR, 'renderer-module/style.css'),
26+
],
2127
varPrefix: '--un-jr-',
2228
})
2329
console.log(`${c.green('✓')} CSS built (${sourceCount} sources, ${(css.length / 1024).toFixed(1)} kB)`)

packages/json-render-ui/src/JsonRender.stories.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { Spec } from '@devframes/json-render'
22
import type { Meta, StoryObj } from '@storybook/vue3-vite'
3-
import { h } from 'vue'
3+
import { h, onMounted, onUnmounted, useTemplateRef } from 'vue'
44
import { baseRegistry } from './registry'
55
import { JsonRenderView } from './renderer'
6+
import jsonRenderDockRenderer from './renderer-module'
67

78
// A no-op RPC — stories don't dispatch real actions.
89
const rpc = { call: async () => undefined }
@@ -20,7 +21,7 @@ const meta: Meta = {
2021
}
2122
export default meta
2223

23-
export const Gallery = story({
24+
const gallerySpec: Spec = {
2425
root: 'root',
2526
elements: {
2627
root: { type: 'Stack', props: { gap: 12 }, children: ['title', 'row', 'card', 'progress', 'table', 'tree'] },
@@ -35,7 +36,9 @@ export const Gallery = story({
3536
table: { type: 'DataTable', props: { rows: [{ id: 1, name: 'a' }, { id: 2, name: 'b' }] }, children: [] },
3637
tree: { type: 'Tree', props: { data: { a: 1, b: [true, 'x'] } }, children: [] },
3738
},
38-
})
39+
}
40+
41+
export const Gallery = story(gallerySpec)
3942

4043
export const Controls = story({
4144
root: 'root',
@@ -103,3 +106,33 @@ export const SubsetRegistry: StoryObj = story(
103106
},
104107
{ registry: subsetRegistry },
105108
)
109+
110+
const dockRendererContext = {
111+
rpc: { call: rpc.call, connectionMeta: undefined },
112+
} as unknown as Parameters<typeof jsonRenderDockRenderer>[0]['context']
113+
114+
/** Mounts the shipped dock renderer so the story exercises its shadow root and adopted stylesheet. */
115+
export const InShadowRoot: StoryObj = {
116+
render: () => ({
117+
setup() {
118+
const host = useTemplateRef<HTMLDivElement>('host')
119+
let dispose: (() => void) | undefined
120+
onMounted(async () => {
121+
const instance = await jsonRenderDockRenderer({
122+
entry: {
123+
id: 'story',
124+
title: 'Story',
125+
icon: 'ph:cube-duotone',
126+
type: 'json-render',
127+
view: { spec: gallerySpec },
128+
},
129+
container: host.value!,
130+
context: dockRendererContext,
131+
})
132+
dispose = instance.dispose
133+
})
134+
onUnmounted(() => dispose?.())
135+
return () => h('div', { ref: 'host', class: 'w-full h-80 rounded-lg bg-grid' })
136+
},
137+
}),
138+
}

packages/json-render-ui/src/renderer-module/index.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,32 @@ const jsonRenderDockRenderer: JsonRenderDockRenderer = async ({ entry, container
4646
shadow.append(style)
4747
}
4848

49-
// Carries the `.dark`/`.light` class that class-based utilities resolve
50-
// against (kept in sync with the viewer's container class), and the native
51-
// `color-scheme` for scrollbars and form controls.
49+
// Keep the scheme class on an ancestor. Wind3 emits descendant selectors
50+
// such as `.dark .bg-base`, which do not match an element carrying both
51+
// classes itself.
52+
const colorSchemeRoot = document.createElement('div')
53+
colorSchemeRoot.style.display = 'contents'
5254
const root = document.createElement('div')
53-
root.className = 'w-full h-full of-auto p4 bg-base color-base font-sans text-sm'
55+
root.className = 'devframes-json-render-scroll-root w-full h-full of-auto p4 color-base font-sans text-sm'
5456
const syncScheme = (): void => {
5557
const dark = isDarkFor(container)
56-
root.classList.toggle('dark', dark)
57-
root.classList.toggle('light', !dark)
58-
root.style.colorScheme = dark ? 'dark' : 'light'
58+
colorSchemeRoot.classList.toggle('dark', dark)
59+
colorSchemeRoot.classList.toggle('light', !dark)
60+
colorSchemeRoot.style.colorScheme = dark ? 'dark' : 'light'
5961
}
6062
syncScheme()
6163
const observer = new MutationObserver(syncScheme)
6264
observer.observe(container, { attributes: true, attributeFilter: ['class'] })
6365
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
64-
shadow.append(root)
66+
colorSchemeRoot.append(root)
67+
shadow.append(colorSchemeRoot)
6568

6669
const instance = await inner({ entry, container: root, context })
6770
return {
6871
dispose() {
6972
observer.disconnect()
7073
instance.dispose?.()
71-
root.remove()
74+
colorSchemeRoot.remove()
7275
},
7376
}
7477
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.devframes-json-render-scroll-root {
2+
scrollbar-gutter: stable;
3+
}

0 commit comments

Comments
 (0)