@ponchia/ui 0.4.0 → 0.4.1
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/CHANGELOG.md +128 -10
- package/README.md +20 -13
- package/behaviors/index.d.ts +3 -1
- package/behaviors/index.js +179 -106
- package/classes/index.d.ts +40 -0
- package/classes/index.js +41 -0
- package/classes/vscode.css-custom-data.json +18 -14
- package/css/disclosure.css +29 -0
- package/css/dots.css +2 -0
- package/css/feedback.css +53 -0
- package/css/motion.css +88 -0
- package/css/overlay.css +52 -1
- package/css/report.css +382 -0
- package/css/tokens.css +56 -26
- package/dist/bronto.css +1 -1
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/motion.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/report.css +1 -0
- package/dist/css/tokens.css +1 -1
- package/docs/adr/0001-color-system.md +26 -27
- package/docs/adr/0002-scope-and-2026-baseline.md +104 -0
- package/docs/adr/0003-theme-model.md +94 -0
- package/docs/contrast.md +42 -42
- package/docs/reference.md +112 -15
- package/docs/reporting.md +270 -0
- package/docs/stability.md +37 -0
- package/docs/theming.md +18 -6
- package/docs/usage.md +21 -3
- package/llms.txt +61 -4
- package/package.json +28 -3
- package/qwik/index.d.ts +55 -0
- package/qwik/index.js +129 -0
- package/react/index.d.ts +35 -14
- package/react/index.js +22 -5
- package/solid/index.d.ts +35 -14
- package/solid/index.js +21 -3
- package/tokens/index.d.ts +3 -3
- package/tokens/index.js +16 -14
- package/tokens/index.json +32 -28
- package/tokens/resolved.json +34 -32
- package/tokens/tokens.dtcg.json +22 -14
package/llms.txt
CHANGED
|
@@ -20,6 +20,12 @@ CSS (pick one; do not import both):
|
|
|
20
20
|
@import '@ponchia/ui/css'; /* css/core.css — @import fan-out, needs a bundler */
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
`import '@ponchia/ui'` is supported only as a CSS side-effect import in a
|
|
24
|
+
CSS-aware bundler. Do not import the package root from Node/runtime JS; use the
|
|
25
|
+
explicit JS subpaths below.
|
|
26
|
+
|
|
27
|
+
JS subpaths are ESM-only. In CommonJS, use dynamic `import()`.
|
|
28
|
+
|
|
23
29
|
Everything lands in `@layer bronto`. Consumer styles in no layer (or a
|
|
24
30
|
later layer) always win over framework styles regardless of selector
|
|
25
31
|
specificity — this is the intended override mechanism; do not fight it
|
|
@@ -46,11 +52,13 @@ Optional vanilla behaviors (theme toggle, etc. — SSR-safe, tree-shakeable):
|
|
|
46
52
|
import { applyStoredTheme } from '@ponchia/ui/behaviors';
|
|
47
53
|
```
|
|
48
54
|
|
|
49
|
-
For React/Solid, optional thin hook bindings over those behaviors (peer
|
|
50
|
-
`react` / `solid-js`, optional — core stays zero-dep):
|
|
55
|
+
For React/Solid/Qwik, optional thin hook bindings over those behaviors (peer
|
|
56
|
+
deps `react` / `solid-js` / `@builder.io/qwik`, optional — core stays zero-dep):
|
|
51
57
|
|
|
52
58
|
```js
|
|
53
|
-
import { useDialog, useToast } from '@ponchia/ui/react'; // or '@ponchia/ui/solid'
|
|
59
|
+
import { useDialog, useToast } from '@ponchia/ui/react'; // or '@ponchia/ui/solid' or '@ponchia/ui/qwik'
|
|
60
|
+
// React: scope with { root: reactRef } or a resolver, not { root: ref.current } during render.
|
|
61
|
+
// Qwik: hooks run in useVisibleTask$; scope with a Qwik signal — useDialog({ root: useSignal() }).
|
|
54
62
|
```
|
|
55
63
|
|
|
56
64
|
Optional display glyphs — dot-matrix bitmaps on the `.ui-dotmatrix` primitive
|
|
@@ -89,6 +97,47 @@ simulated protan/deutan/tritan), `--chart-seq-*` (sequential), `--chart-div-*`
|
|
|
89
97
|
(diverging), and `--chart-pattern-1..8` (dot-matrix fills — pair colour N with
|
|
90
98
|
pattern N; colour is never the sole signal). Details in `docs/theming.md`.
|
|
91
99
|
|
|
100
|
+
Optional static report layer for LLM-authored HTML reports — opt-in, never in
|
|
101
|
+
the default bundle. Pair it with the normal bundle, and add data-viz only when
|
|
102
|
+
the report contains charts. For standalone browser HTML, use real URLs; package
|
|
103
|
+
specifiers only work after a bundler rewrites them:
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/bronto.css" />
|
|
107
|
+
<link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/css/report.css" />
|
|
108
|
+
<link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/css/dataviz.css" />
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
In Vite/webpack/etc., import `@ponchia/ui`, `@ponchia/ui/css/report.css`, and
|
|
112
|
+
`@ponchia/ui/css/dataviz.css` instead.
|
|
113
|
+
|
|
114
|
+
Canonical report skeleton:
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<main class="ui-report ui-report--numbered">
|
|
118
|
+
<header class="ui-report__cover">
|
|
119
|
+
<p class="ui-eyebrow">Report</p>
|
|
120
|
+
<h1 class="ui-report__title">System review</h1>
|
|
121
|
+
<p class="ui-report__subtitle">Static HTML, Chromium PDF-ready.</p>
|
|
122
|
+
<ol class="ui-report__meta"><li>2026-06-01</li><li>Generated</li></ol>
|
|
123
|
+
</header>
|
|
124
|
+
<section class="ui-report__section">
|
|
125
|
+
<h2 class="ui-report__section-head">Executive summary</h2>
|
|
126
|
+
<div class="ui-report__summary">
|
|
127
|
+
<div class="ui-prose"><p>Use prose only for narrative body content.</p></div>
|
|
128
|
+
</div>
|
|
129
|
+
</section>
|
|
130
|
+
</main>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Report rules for agents: static output by default; do not initialize behaviors
|
|
134
|
+
over untrusted generated content; sanitize arbitrary LLM/CMS/user HTML before
|
|
135
|
+
rendering; use semantic headings; give tables captions and header cells; give
|
|
136
|
+
charts captions, legends/direct labels, and fallback data; use
|
|
137
|
+
`ui-chart__plot`/`ui-chart__bar`/`ui-chart__fill` for simple static bar charts;
|
|
138
|
+
use `ui-report__section--unnumbered` for appendices in numbered reports; never
|
|
139
|
+
use raw chromatic inline colors. Full cookbook: `docs/reporting.md`.
|
|
140
|
+
|
|
92
141
|
## Authoritative offline references (shipped in this package)
|
|
93
142
|
|
|
94
143
|
Read these from `node_modules/@ponchia/ui/` — no network needed:
|
|
@@ -101,9 +150,14 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
|
|
|
101
150
|
the same source as the types and CI-drift-checked.
|
|
102
151
|
- `docs/theming.md` — the token contract: which tokens to override, the
|
|
103
152
|
light/dark model, and the rationed-accent rule.
|
|
153
|
+
- `docs/stability.md` — the public API matrix: stable vs internal surfaces,
|
|
154
|
+
root-import contract, behavior trust boundary, and glyph-mask CSP note.
|
|
104
155
|
- `docs/usage.md` — the decision guide: badge vs chip vs dot, default
|
|
105
156
|
density, prose-in-card, when to add a behavior. Read this before
|
|
106
157
|
re-implementing UI the framework already has an opinion about.
|
|
158
|
+
- `docs/reporting.md` — static/PDF-first report cookbook for LLM-authored
|
|
159
|
+
reports, including report skeletons, chart rules, print utilities, and safety
|
|
160
|
+
boundaries.
|
|
107
161
|
- `docs/contrast.md` — the published, CI-gated WCAG 2.1 contrast matrix
|
|
108
162
|
for every contractual token pairing, per theme. Generated from the
|
|
109
163
|
resolved palette; the build fails below the declared floor.
|
|
@@ -123,7 +177,7 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
|
|
|
123
177
|
|
|
124
178
|
- Live kitchen-sink demo (every component, light/dark, RTL): https://ponchia.github.io/bronto-ui/
|
|
125
179
|
- Architecture & the CSS-first decision: https://github.com/Ponchia/bronto-ui/blob/main/docs/architecture.md
|
|
126
|
-
- Framework integration guides (Astro / SvelteKit / React-Solid / vanilla):
|
|
180
|
+
- Framework integration guides (Astro / SvelteKit / React-Solid-Qwik / vanilla):
|
|
127
181
|
https://github.com/Ponchia/bronto-ui/tree/main/docs/getting-started
|
|
128
182
|
|
|
129
183
|
## Rules an agent should respect
|
|
@@ -131,6 +185,9 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
|
|
|
131
185
|
- Never invent `ui-*` class names. Every valid class is a member of
|
|
132
186
|
`cls` in `classes/index.d.ts`; if it is not there, it does not exist.
|
|
133
187
|
- Prefer the `ui.*()` recipes over hand-concatenating modifier strings.
|
|
188
|
+
Report/layout classes added in 0.4.1 are the exception: use `cls.report*`,
|
|
189
|
+
`cls.chart*`, `cls.printOnly`, or literal documented class names because
|
|
190
|
+
there are no `ui.report()` / `ui.chart()` recipes yet.
|
|
134
191
|
- Override framework styles by writing your own rules outside
|
|
135
192
|
`@layer bronto` — not with higher specificity or `!important`.
|
|
136
193
|
- Color is tiered (ADR-0001): neutral canvas · one rationed accent · locked
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ponchia/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CSS-first, framework-agnostic UI framework — token-driven restraint (monochrome by default, one rationed accent), OKLCH colorways, a dot-matrix icon set, a colourblind-safe data-viz palette, dot-matrix motifs, zero runtime dependencies.",
|
|
6
6
|
"keywords": [
|
|
@@ -48,14 +48,19 @@
|
|
|
48
48
|
"glyphs",
|
|
49
49
|
"react",
|
|
50
50
|
"solid",
|
|
51
|
+
"qwik",
|
|
51
52
|
"shiki",
|
|
52
53
|
"llms.txt",
|
|
53
54
|
"CHANGELOG.md",
|
|
54
55
|
"docs/reference.md",
|
|
55
56
|
"docs/theming.md",
|
|
56
57
|
"docs/contrast.md",
|
|
58
|
+
"docs/stability.md",
|
|
57
59
|
"docs/usage.md",
|
|
58
|
-
"docs/
|
|
60
|
+
"docs/reporting.md",
|
|
61
|
+
"docs/adr/0001-color-system.md",
|
|
62
|
+
"docs/adr/0002-scope-and-2026-baseline.md",
|
|
63
|
+
"docs/adr/0003-theme-model.md"
|
|
59
64
|
],
|
|
60
65
|
"style": "./dist/bronto.css",
|
|
61
66
|
"scripts": {
|
|
@@ -63,6 +68,7 @@
|
|
|
63
68
|
"lint:fix": "stylelint \"css/**/*.css\" --fix",
|
|
64
69
|
"format": "prettier --write .",
|
|
65
70
|
"check:format": "prettier --check .",
|
|
71
|
+
"size:report": "node scripts/size-report.mjs",
|
|
66
72
|
"tokens:build": "node scripts/gen-tokens-json.mjs",
|
|
67
73
|
"dtcg:build": "node scripts/gen-dtcg.mjs",
|
|
68
74
|
"resolved:build": "node scripts/gen-resolved.mjs",
|
|
@@ -84,6 +90,7 @@
|
|
|
84
90
|
"check:color-policy": "node scripts/check-color-policy.mjs",
|
|
85
91
|
"check:skins": "node scripts/check-skins.mjs",
|
|
86
92
|
"check:charts": "node scripts/check-charts.mjs",
|
|
93
|
+
"check:report": "node scripts/check-report.mjs",
|
|
87
94
|
"check:types": "tsc -p tsconfig.json",
|
|
88
95
|
"check:dtcg": "node scripts/check-dtcg.mjs",
|
|
89
96
|
"check:resolved": "node scripts/check-resolved.mjs",
|
|
@@ -94,26 +101,34 @@
|
|
|
94
101
|
"check:reference": "node scripts/check-reference.mjs",
|
|
95
102
|
"check:contrast": "node scripts/check-contrast.mjs",
|
|
96
103
|
"check:vscode": "node scripts/check-vscode-data.mjs",
|
|
97
|
-
"check": "npm run lint && npm run check:format && npm run check:exports && npm run check:tokens && npm run check:classes && npm run check:dts && npm run check:types && npm run check:dtcg && npm run check:resolved && npm run check:shiki && npm run check:dist && npm run check:pack && npm run check:release && npm run check:reference && npm run check:contrast && npm run check:vscode && npm run check:behaviors && npm run check:bindings && npm run check:glyphs && npm run check:color-policy && npm run check:skins && npm run check:charts",
|
|
104
|
+
"check": "npm run lint && npm run check:format && npm run check:exports && npm run check:tokens && npm run check:classes && npm run check:dts && npm run check:types && npm run check:dtcg && npm run check:resolved && npm run check:shiki && npm run check:dist && npm run check:pack && npm run check:release && npm run check:reference && npm run check:contrast && npm run check:vscode && npm run check:behaviors && npm run check:bindings && npm run check:glyphs && npm run check:color-policy && npm run check:skins && npm run check:charts && npm run check:report",
|
|
98
105
|
"test": "node --test \"test/*.test.mjs\"",
|
|
99
106
|
"prepack": "npm run tokens:build && npm run dtcg:build && npm run resolved:build && npm run dts:build && npm run reference:build && npm run contrast:build && npm run vscode:build && npm run skins:build && npm run charts:build && npm run dist:build && npm run glyphs:build",
|
|
100
107
|
"prepublishOnly": "npm run check && npm test"
|
|
101
108
|
},
|
|
102
109
|
"devDependencies": {
|
|
103
110
|
"@axe-core/playwright": "^4.11.3",
|
|
111
|
+
"@builder.io/qwik": "^1.20.0",
|
|
104
112
|
"@playwright/test": "^1.60.0",
|
|
105
113
|
"jsdom": "^29.1.1",
|
|
106
114
|
"prettier": "^3.8.3",
|
|
115
|
+
"react": "^19.2.6",
|
|
116
|
+
"react-dom": "^19.2.6",
|
|
117
|
+
"solid-js": "^1.9.13",
|
|
107
118
|
"stylelint": "^17.11.1",
|
|
108
119
|
"stylelint-config-standard": "^40.0.0",
|
|
109
120
|
"stylelint-use-logical": "^2.1.3",
|
|
110
121
|
"typescript": "^6.0.3"
|
|
111
122
|
},
|
|
112
123
|
"peerDependencies": {
|
|
124
|
+
"@builder.io/qwik": ">=1.5",
|
|
113
125
|
"react": ">=18",
|
|
114
126
|
"solid-js": ">=1.6"
|
|
115
127
|
},
|
|
116
128
|
"peerDependenciesMeta": {
|
|
129
|
+
"@builder.io/qwik": {
|
|
130
|
+
"optional": true
|
|
131
|
+
},
|
|
117
132
|
"react": {
|
|
118
133
|
"optional": true
|
|
119
134
|
},
|
|
@@ -146,6 +161,7 @@
|
|
|
146
161
|
"./css/app.css": "./dist/css/app.css",
|
|
147
162
|
"./css/skins.css": "./dist/css/skins.css",
|
|
148
163
|
"./css/dataviz.css": "./dist/css/dataviz.css",
|
|
164
|
+
"./css/report.css": "./dist/css/report.css",
|
|
149
165
|
"./css/unlayered/tokens.css": "./css/tokens.css",
|
|
150
166
|
"./css/unlayered/fonts.css": "./css/fonts.css",
|
|
151
167
|
"./css/unlayered/base.css": "./css/base.css",
|
|
@@ -163,6 +179,7 @@
|
|
|
163
179
|
"./css/unlayered/app.css": "./css/app.css",
|
|
164
180
|
"./css/unlayered/skins.css": "./css/skins.css",
|
|
165
181
|
"./css/unlayered/dataviz.css": "./css/dataviz.css",
|
|
182
|
+
"./css/unlayered/report.css": "./css/report.css",
|
|
166
183
|
"./tokens": {
|
|
167
184
|
"types": "./tokens/index.d.ts",
|
|
168
185
|
"default": "./tokens/index.js"
|
|
@@ -176,8 +193,12 @@
|
|
|
176
193
|
"./docs/reference.md": "./docs/reference.md",
|
|
177
194
|
"./docs/theming.md": "./docs/theming.md",
|
|
178
195
|
"./docs/contrast.md": "./docs/contrast.md",
|
|
196
|
+
"./docs/stability.md": "./docs/stability.md",
|
|
179
197
|
"./docs/usage.md": "./docs/usage.md",
|
|
198
|
+
"./docs/reporting.md": "./docs/reporting.md",
|
|
180
199
|
"./docs/adr/0001-color-system.md": "./docs/adr/0001-color-system.md",
|
|
200
|
+
"./docs/adr/0002-scope-and-2026-baseline.md": "./docs/adr/0002-scope-and-2026-baseline.md",
|
|
201
|
+
"./docs/adr/0003-theme-model.md": "./docs/adr/0003-theme-model.md",
|
|
181
202
|
"./classes": {
|
|
182
203
|
"types": "./classes/index.d.ts",
|
|
183
204
|
"default": "./classes/index.js"
|
|
@@ -198,6 +219,10 @@
|
|
|
198
219
|
"types": "./solid/index.d.ts",
|
|
199
220
|
"default": "./solid/index.js"
|
|
200
221
|
},
|
|
222
|
+
"./qwik": {
|
|
223
|
+
"types": "./qwik/index.d.ts",
|
|
224
|
+
"default": "./qwik/index.js"
|
|
225
|
+
},
|
|
201
226
|
"./skins": {
|
|
202
227
|
"types": "./tokens/skins.d.ts",
|
|
203
228
|
"default": "./tokens/skins.js"
|
package/qwik/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** @ponchia/ui/qwik — thin Qwik bindings over the SSR-safe behaviors.
|
|
2
|
+
* Optional peer dep `@builder.io/qwik`. Hooks run a delegated behavior on
|
|
3
|
+
* visible and register cleanup on dispose (via `useVisibleTask$`); they take
|
|
4
|
+
* the same options as the behavior and return void. See behaviors/index.d.ts. */
|
|
5
|
+
import type { Cleanup, DelegateOpts, ThemeStorageOpts, ToastOpts } from '../behaviors/index.js';
|
|
6
|
+
|
|
7
|
+
export type BrontoBindingRoot =
|
|
8
|
+
| Document
|
|
9
|
+
| Element
|
|
10
|
+
// Qwik `useSignal()` (and any { value } / { current } ref shape).
|
|
11
|
+
| { value: Document | Element | null | undefined }
|
|
12
|
+
| { current: Document | Element | null | undefined }
|
|
13
|
+
| (() => Document | Element | null | undefined)
|
|
14
|
+
| null
|
|
15
|
+
| undefined;
|
|
16
|
+
|
|
17
|
+
export type BrontoBindingOpts<T extends DelegateOpts = DelegateOpts> = Omit<T, 'root'> & {
|
|
18
|
+
root?: BrontoBindingRoot;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type BrontoBindingOptsResolver<T extends DelegateOpts = DelegateOpts> =
|
|
22
|
+
| BrontoBindingOpts<T>
|
|
23
|
+
| (() => BrontoBindingOpts<T> | null | undefined)
|
|
24
|
+
| null
|
|
25
|
+
| undefined;
|
|
26
|
+
|
|
27
|
+
/** Run any delegated behavior for the component's lifetime (init on visible,
|
|
28
|
+
* its returned cleanup on dispose). Prefer the specific `use*` hooks in Qwik
|
|
29
|
+
* so the optimizer can serialize the task; this generic form is safe only with
|
|
30
|
+
* an optimizer-visible (module-imported) behavior. */
|
|
31
|
+
export declare function useBrontoBehavior(
|
|
32
|
+
init: (opts?: DelegateOpts) => Cleanup | void,
|
|
33
|
+
opts?: BrontoBindingOptsResolver,
|
|
34
|
+
): void;
|
|
35
|
+
|
|
36
|
+
export declare function useThemeToggle(
|
|
37
|
+
opts?: BrontoBindingOptsResolver<ThemeStorageOpts & DelegateOpts>,
|
|
38
|
+
): void;
|
|
39
|
+
export declare function useDismissible(opts?: BrontoBindingOptsResolver): void;
|
|
40
|
+
export declare function useDisclosure(opts?: BrontoBindingOptsResolver): void;
|
|
41
|
+
export declare function useMenu(opts?: BrontoBindingOptsResolver): void;
|
|
42
|
+
export declare function useFormValidation(opts?: BrontoBindingOptsResolver): void;
|
|
43
|
+
export declare function useCombobox(opts?: BrontoBindingOptsResolver): void;
|
|
44
|
+
export declare function usePopover(opts?: BrontoBindingOptsResolver): void;
|
|
45
|
+
export declare function useTableSort(opts?: BrontoBindingOptsResolver): void;
|
|
46
|
+
export declare function useTabs(opts?: BrontoBindingOptsResolver): void;
|
|
47
|
+
export declare function useDialog(opts?: BrontoBindingOptsResolver): void;
|
|
48
|
+
export declare function useCarousel(opts?: BrontoBindingOptsResolver): void;
|
|
49
|
+
export declare function useDotGlyph(opts?: BrontoBindingOptsResolver): void;
|
|
50
|
+
|
|
51
|
+
/** The `toast()` imperative (no lifecycle of its own). */
|
|
52
|
+
export declare function useToast(): (message: string, opts?: ToastOpts) => Cleanup;
|
|
53
|
+
|
|
54
|
+
export { applyStoredTheme } from '../behaviors/index.js';
|
|
55
|
+
export { cls, ui, cx } from '../classes/index.js';
|
package/qwik/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ponchia/ui/qwik — thin Qwik bindings over @ponchia/ui/behaviors.
|
|
3
|
+
*
|
|
4
|
+
* The CSS is the framework; these are *optional* hooks that wrap the SSR-safe
|
|
5
|
+
* vanilla `init*` behaviors in Qwik's client lifecycle. Thin adapters, not a
|
|
6
|
+
* component library (architecture ADR). `@builder.io/qwik` is an optional peer
|
|
7
|
+
* dependency.
|
|
8
|
+
*
|
|
9
|
+
* Qwik is resumable: nothing runs until needed, and our behaviors are exactly
|
|
10
|
+
* the kind of "enhance on the client" glue that fits — they `useVisibleTask$`
|
|
11
|
+
* (run when the owning component first becomes visible) and register cleanup,
|
|
12
|
+
* so a server-rendered page stays zero-JS until interaction. Each hook inlines
|
|
13
|
+
* its specific behavior so the Qwik optimizer can resolve the import inside the
|
|
14
|
+
* extracted task segment.
|
|
15
|
+
*
|
|
16
|
+
* import { component$ } from '@builder.io/qwik';
|
|
17
|
+
* import { useDialog, useToast } from '@ponchia/ui/qwik';
|
|
18
|
+
* export default component$(() => {
|
|
19
|
+
* useDialog(); // wires every .ui-modal under document
|
|
20
|
+
* const toast = useToast();
|
|
21
|
+
* return <button onClick$={() => toast('Saved', { tone: 'success' })}>Save</button>;
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* Scope a behavior to a subtree by passing a Qwik signal:
|
|
25
|
+
* const root = useSignal();
|
|
26
|
+
* useDialog({ root }); // <section ref={root}> … </section>
|
|
27
|
+
*/
|
|
28
|
+
import { useVisibleTask$ } from '@builder.io/qwik';
|
|
29
|
+
import {
|
|
30
|
+
applyStoredTheme,
|
|
31
|
+
initThemeToggle,
|
|
32
|
+
dismissible,
|
|
33
|
+
initDisclosure,
|
|
34
|
+
initMenu,
|
|
35
|
+
initFormValidation,
|
|
36
|
+
initCombobox,
|
|
37
|
+
initPopover,
|
|
38
|
+
initTableSort,
|
|
39
|
+
initTabs,
|
|
40
|
+
initDialog,
|
|
41
|
+
initCarousel,
|
|
42
|
+
initDotGlyph,
|
|
43
|
+
toast,
|
|
44
|
+
} from '../behaviors/index.js';
|
|
45
|
+
|
|
46
|
+
function resolveMaybe(v) {
|
|
47
|
+
return typeof v === 'function' ? v() : v;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function resolveRoot(root) {
|
|
51
|
+
const value = resolveMaybe(root);
|
|
52
|
+
if (value && typeof value === 'object') {
|
|
53
|
+
if ('value' in value) return value.value; // Qwik signal (useSignal)
|
|
54
|
+
if ('current' in value) return value.current; // generic ref shape
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveOpts(opts) {
|
|
60
|
+
const value = resolveMaybe(opts);
|
|
61
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
62
|
+
const root = resolveRoot(value.root);
|
|
63
|
+
return root ? { ...value, root } : { ...value, root: undefined };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Run a delegated behavior on visible and register its cleanup on dispose.
|
|
67
|
+
* `init` and `opts` are resolved inside the visible task, so a Qwik-signal
|
|
68
|
+
* root is read after the element is assigned. Shared, non-QRL, so the
|
|
69
|
+
* optimizer keeps the captured behavior import inside the task segment. */
|
|
70
|
+
function start(init, opts, ctx) {
|
|
71
|
+
const cleanup = init(resolveOpts(opts));
|
|
72
|
+
if (typeof cleanup === 'function') ctx.cleanup(cleanup);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Generic escape hatch. NOTE for Qwik: prefer the specific `use*` hooks
|
|
76
|
+
* below — they inline a statically-imported behavior so the optimizer can
|
|
77
|
+
* serialize the task. Passing a runtime function here is only safe when it
|
|
78
|
+
* is itself optimizer-visible (a module import). */
|
|
79
|
+
export function useBrontoBehavior(init, opts) {
|
|
80
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
81
|
+
useVisibleTask$((ctx) => start(init, opts, ctx));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const useThemeToggle = (opts) =>
|
|
85
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
86
|
+
useVisibleTask$((ctx) => start(initThemeToggle, opts, ctx));
|
|
87
|
+
export const useDismissible = (opts) =>
|
|
88
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
89
|
+
useVisibleTask$((ctx) => start(dismissible, opts, ctx));
|
|
90
|
+
export const useDisclosure = (opts) =>
|
|
91
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
92
|
+
useVisibleTask$((ctx) => start(initDisclosure, opts, ctx));
|
|
93
|
+
export const useMenu = (opts) =>
|
|
94
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
95
|
+
useVisibleTask$((ctx) => start(initMenu, opts, ctx));
|
|
96
|
+
export const useFormValidation = (opts) =>
|
|
97
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
98
|
+
useVisibleTask$((ctx) => start(initFormValidation, opts, ctx));
|
|
99
|
+
export const useCombobox = (opts) =>
|
|
100
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
101
|
+
useVisibleTask$((ctx) => start(initCombobox, opts, ctx));
|
|
102
|
+
export const usePopover = (opts) =>
|
|
103
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
104
|
+
useVisibleTask$((ctx) => start(initPopover, opts, ctx));
|
|
105
|
+
export const useTableSort = (opts) =>
|
|
106
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
107
|
+
useVisibleTask$((ctx) => start(initTableSort, opts, ctx));
|
|
108
|
+
export const useTabs = (opts) =>
|
|
109
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
110
|
+
useVisibleTask$((ctx) => start(initTabs, opts, ctx));
|
|
111
|
+
export const useDialog = (opts) =>
|
|
112
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
113
|
+
useVisibleTask$((ctx) => start(initDialog, opts, ctx));
|
|
114
|
+
export const useCarousel = (opts) =>
|
|
115
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
116
|
+
useVisibleTask$((ctx) => start(initCarousel, opts, ctx));
|
|
117
|
+
export const useDotGlyph = (opts) =>
|
|
118
|
+
// eslint-disable-next-line qwik/no-use-visible-task -- delegated DOM glue + cleanup
|
|
119
|
+
useVisibleTask$((ctx) => start(initDotGlyph, opts, ctx));
|
|
120
|
+
|
|
121
|
+
/** The `toast()` imperative (no lifecycle of its own). */
|
|
122
|
+
export const useToast = () => toast;
|
|
123
|
+
|
|
124
|
+
// No-flash theme application must run before paint — do it in an inline head
|
|
125
|
+
// script, not on visible. Re-exported for manual/SSR-bootstrap use.
|
|
126
|
+
export { applyStoredTheme };
|
|
127
|
+
|
|
128
|
+
// Convenience: the framework-agnostic class contract, re-exported.
|
|
129
|
+
export { cls, ui, cx } from '../classes/index.js';
|
package/react/index.d.ts
CHANGED
|
@@ -9,25 +9,46 @@ import type {
|
|
|
9
9
|
ToastOpts,
|
|
10
10
|
} from '../behaviors/index.js';
|
|
11
11
|
|
|
12
|
+
export type BrontoBindingRoot =
|
|
13
|
+
| Document
|
|
14
|
+
| Element
|
|
15
|
+
| { current: Document | Element | null | undefined }
|
|
16
|
+
| (() => Document | Element | null | undefined)
|
|
17
|
+
| null
|
|
18
|
+
| undefined;
|
|
19
|
+
|
|
20
|
+
export type BrontoBindingOpts<T extends DelegateOpts = DelegateOpts> = Omit<T, 'root'> & {
|
|
21
|
+
root?: BrontoBindingRoot;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type BrontoBindingOptsResolver<T extends DelegateOpts = DelegateOpts> =
|
|
25
|
+
| BrontoBindingOpts<T>
|
|
26
|
+
| (() => BrontoBindingOpts<T> | null | undefined)
|
|
27
|
+
| null
|
|
28
|
+
| undefined;
|
|
29
|
+
|
|
12
30
|
/** Run any delegated behavior for the component's lifetime (init on mount,
|
|
13
|
-
* its returned cleanup on unmount). The behavior is run once.
|
|
31
|
+
* its returned cleanup on unmount). The behavior is run once. Options resolve
|
|
32
|
+
* on mount, so scoped roots may be React refs or resolver callbacks. */
|
|
14
33
|
export declare function useBrontoBehavior(
|
|
15
34
|
init: (opts?: DelegateOpts) => Cleanup | void,
|
|
16
|
-
opts?:
|
|
35
|
+
opts?: BrontoBindingOptsResolver,
|
|
17
36
|
): void;
|
|
18
37
|
|
|
19
|
-
export declare function useThemeToggle(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export declare function
|
|
23
|
-
export declare function
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function
|
|
26
|
-
export declare function
|
|
27
|
-
export declare function
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function
|
|
30
|
-
export declare function
|
|
38
|
+
export declare function useThemeToggle(
|
|
39
|
+
opts?: BrontoBindingOptsResolver<ThemeStorageOpts & DelegateOpts>,
|
|
40
|
+
): void;
|
|
41
|
+
export declare function useDismissible(opts?: BrontoBindingOptsResolver): void;
|
|
42
|
+
export declare function useDisclosure(opts?: BrontoBindingOptsResolver): void;
|
|
43
|
+
export declare function useMenu(opts?: BrontoBindingOptsResolver): void;
|
|
44
|
+
export declare function useFormValidation(opts?: BrontoBindingOptsResolver): void;
|
|
45
|
+
export declare function useCombobox(opts?: BrontoBindingOptsResolver): void;
|
|
46
|
+
export declare function usePopover(opts?: BrontoBindingOptsResolver): void;
|
|
47
|
+
export declare function useTableSort(opts?: BrontoBindingOptsResolver): void;
|
|
48
|
+
export declare function useTabs(opts?: BrontoBindingOptsResolver): void;
|
|
49
|
+
export declare function useDialog(opts?: BrontoBindingOptsResolver): void;
|
|
50
|
+
export declare function useCarousel(opts?: BrontoBindingOptsResolver): void;
|
|
51
|
+
export declare function useDotGlyph(opts?: BrontoBindingOptsResolver): void;
|
|
31
52
|
|
|
32
53
|
/** The `toast()` imperative (no lifecycle of its own). */
|
|
33
54
|
export declare function useToast(): (message: string, opts?: ToastOpts) => Cleanup;
|
package/react/index.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* the architecture ADR. `react` is an optional peer dependency.
|
|
8
8
|
*
|
|
9
9
|
* The behaviors delegate from a root (default `document`), so call a hook once
|
|
10
|
-
* near the relevant subtree; pass `{ root: ref
|
|
11
|
-
*
|
|
10
|
+
* near the relevant subtree; pass `{ root: ref }` or a resolver callback to
|
|
11
|
+
* scope it. The options resolve on mount, after refs have been assigned.
|
|
12
12
|
*
|
|
13
13
|
* import { useDialog, useToast } from '@ponchia/ui/react';
|
|
14
14
|
* function App() {
|
|
@@ -35,11 +35,28 @@ import {
|
|
|
35
35
|
toast,
|
|
36
36
|
} from '../behaviors/index.js';
|
|
37
37
|
|
|
38
|
+
function resolveMaybe(v) {
|
|
39
|
+
return typeof v === 'function' ? v() : v;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resolveRoot(root) {
|
|
43
|
+
const value = resolveMaybe(root);
|
|
44
|
+
if (value && typeof value === 'object' && 'current' in value) return value.current;
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveOpts(opts) {
|
|
49
|
+
const value = resolveMaybe(opts);
|
|
50
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
51
|
+
const root = resolveRoot(value.root);
|
|
52
|
+
return root ? { ...value, root } : { ...value, root: undefined };
|
|
53
|
+
}
|
|
54
|
+
|
|
38
55
|
/** Run a delegated behavior for the component's lifetime (init on mount, its
|
|
39
|
-
* returned cleanup on unmount). The behavior is run once; `opts`
|
|
40
|
-
* on mount
|
|
56
|
+
* returned cleanup on unmount). The behavior is run once; `opts` resolves
|
|
57
|
+
* on mount so refs are usable for scoped roots. */
|
|
41
58
|
export function useBrontoBehavior(init, opts) {
|
|
42
|
-
useEffect(() => init(opts), []); // eslint-disable-line react-hooks/exhaustive-deps -- delegated once on mount
|
|
59
|
+
useEffect(() => init(resolveOpts(opts)), []); // eslint-disable-line react-hooks/exhaustive-deps -- delegated once on mount
|
|
43
60
|
}
|
|
44
61
|
|
|
45
62
|
export const useThemeToggle = (opts) => useBrontoBehavior(initThemeToggle, opts);
|
package/solid/index.d.ts
CHANGED
|
@@ -9,25 +9,46 @@ import type {
|
|
|
9
9
|
ToastOpts,
|
|
10
10
|
} from '../behaviors/index.js';
|
|
11
11
|
|
|
12
|
+
export type BrontoBindingRoot =
|
|
13
|
+
| Document
|
|
14
|
+
| Element
|
|
15
|
+
| { current: Document | Element | null | undefined }
|
|
16
|
+
| (() => Document | Element | null | undefined)
|
|
17
|
+
| null
|
|
18
|
+
| undefined;
|
|
19
|
+
|
|
20
|
+
export type BrontoBindingOpts<T extends DelegateOpts = DelegateOpts> = Omit<T, 'root'> & {
|
|
21
|
+
root?: BrontoBindingRoot;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type BrontoBindingOptsResolver<T extends DelegateOpts = DelegateOpts> =
|
|
25
|
+
| BrontoBindingOpts<T>
|
|
26
|
+
| (() => BrontoBindingOpts<T> | null | undefined)
|
|
27
|
+
| null
|
|
28
|
+
| undefined;
|
|
29
|
+
|
|
12
30
|
/** Run any delegated behavior for the component's lifetime (init on mount,
|
|
13
|
-
* its returned cleanup on dispose).
|
|
31
|
+
* its returned cleanup on dispose). The behavior is run once. Options resolve
|
|
32
|
+
* on mount, so scoped roots may be refs or resolver callbacks. */
|
|
14
33
|
export declare function useBrontoBehavior(
|
|
15
34
|
init: (opts?: DelegateOpts) => Cleanup | void,
|
|
16
|
-
opts?:
|
|
35
|
+
opts?: BrontoBindingOptsResolver,
|
|
17
36
|
): void;
|
|
18
37
|
|
|
19
|
-
export declare function useThemeToggle(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export declare function
|
|
23
|
-
export declare function
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function
|
|
26
|
-
export declare function
|
|
27
|
-
export declare function
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function
|
|
30
|
-
export declare function
|
|
38
|
+
export declare function useThemeToggle(
|
|
39
|
+
opts?: BrontoBindingOptsResolver<ThemeStorageOpts & DelegateOpts>,
|
|
40
|
+
): void;
|
|
41
|
+
export declare function useDismissible(opts?: BrontoBindingOptsResolver): void;
|
|
42
|
+
export declare function useDisclosure(opts?: BrontoBindingOptsResolver): void;
|
|
43
|
+
export declare function useMenu(opts?: BrontoBindingOptsResolver): void;
|
|
44
|
+
export declare function useFormValidation(opts?: BrontoBindingOptsResolver): void;
|
|
45
|
+
export declare function useCombobox(opts?: BrontoBindingOptsResolver): void;
|
|
46
|
+
export declare function usePopover(opts?: BrontoBindingOptsResolver): void;
|
|
47
|
+
export declare function useTableSort(opts?: BrontoBindingOptsResolver): void;
|
|
48
|
+
export declare function useTabs(opts?: BrontoBindingOptsResolver): void;
|
|
49
|
+
export declare function useDialog(opts?: BrontoBindingOptsResolver): void;
|
|
50
|
+
export declare function useCarousel(opts?: BrontoBindingOptsResolver): void;
|
|
51
|
+
export declare function useDotGlyph(opts?: BrontoBindingOptsResolver): void;
|
|
31
52
|
|
|
32
53
|
/** The `toast()` imperative (no lifecycle of its own). */
|
|
33
54
|
export declare function useToast(): (message: string, opts?: ToastOpts) => Cleanup;
|
package/solid/index.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* `solid-js` is an optional peer dependency.
|
|
8
8
|
*
|
|
9
9
|
* Behaviors delegate from a root (default `document`); call a primitive once in
|
|
10
|
-
* a component that owns the relevant subtree,
|
|
10
|
+
* a component that owns the relevant subtree, and pass a root resolver to scope
|
|
11
|
+
* it when the element is assigned by Solid's ref lifecycle.
|
|
11
12
|
*
|
|
12
13
|
* import { useDialog, useToast } from '@ponchia/ui/solid';
|
|
13
14
|
* function App() {
|
|
@@ -34,11 +35,28 @@ import {
|
|
|
34
35
|
toast,
|
|
35
36
|
} from '../behaviors/index.js';
|
|
36
37
|
|
|
38
|
+
function resolveMaybe(v) {
|
|
39
|
+
return typeof v === 'function' ? v() : v;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resolveRoot(root) {
|
|
43
|
+
const value = resolveMaybe(root);
|
|
44
|
+
if (value && typeof value === 'object' && 'current' in value) return value.current;
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveOpts(opts) {
|
|
49
|
+
const value = resolveMaybe(opts);
|
|
50
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
51
|
+
const root = resolveRoot(value.root);
|
|
52
|
+
return root ? { ...value, root } : { ...value, root: undefined };
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
/** Run a delegated behavior for the component's lifetime (init on mount, its
|
|
38
|
-
* returned cleanup on dispose). */
|
|
56
|
+
* returned cleanup on dispose). Options resolve on mount, after refs exist. */
|
|
39
57
|
export function useBrontoBehavior(init, opts) {
|
|
40
58
|
onMount(() => {
|
|
41
|
-
const cleanup = init(opts);
|
|
59
|
+
const cleanup = init(resolveOpts(opts));
|
|
42
60
|
if (typeof cleanup === 'function') onCleanup(cleanup);
|
|
43
61
|
});
|
|
44
62
|
}
|