@sylwellsoftware/fray 0.7.0 → 1.0.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/README.md +264 -204
- package/colors/README.md +26 -23
- package/colors/gray/colors.css +1 -42
- package/colors/green/colors.css +1 -42
- package/colors/iceblue/colors.css +5 -43
- package/colors/ocean/colors.css +1 -42
- package/colors/orange/colors.css +1 -42
- package/colors/purple/colors.css +1 -42
- package/colors/red/colors.css +1 -42
- package/colors/yellow/colors.css +1 -42
- package/dist/Components/Placeholder.d.ts +1 -2
- package/dist/Components/Placeholder.d.ts.map +1 -1
- package/dist/Components/component.d.ts +15 -7
- package/dist/Components/component.d.ts.map +1 -1
- package/dist/Components/data/descriptionList.d.ts +3 -1
- package/dist/Components/data/descriptionList.d.ts.map +1 -1
- package/dist/Components/data/listview/listview.d.ts +1 -2
- package/dist/Components/data/listview/listview.d.ts.map +1 -1
- package/dist/Components/data/table/DataTable.d.ts +1 -1
- package/dist/Components/data/table/DataTable.d.ts.map +1 -1
- package/dist/Components/data/table/FilterPanel.d.ts +2 -4
- package/dist/Components/data/table/FilterPanel.d.ts.map +1 -1
- package/dist/Components/data/table/TableHeader.d.ts +2 -0
- package/dist/Components/data/table/TableHeader.d.ts.map +1 -1
- package/dist/Components/data/table/TableHeaderCell.d.ts +1 -1
- package/dist/Components/data/table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Components/data/treeview/treeitem.d.ts +1 -0
- package/dist/Components/data/treeview/treeitem.d.ts.map +1 -1
- package/dist/Components/data/treeview/treeview.d.ts +2 -1
- package/dist/Components/data/treeview/treeview.d.ts.map +1 -1
- package/dist/Components/dialog/dialog.d.ts +6 -2
- package/dist/Components/dialog/dialog.d.ts.map +1 -1
- package/dist/Components/layout/header.d.ts +19 -0
- package/dist/Components/layout/header.d.ts.map +1 -0
- package/dist/Components/layout/panel.d.ts +7 -3
- package/dist/Components/layout/panel.d.ts.map +1 -1
- package/dist/Components/layout/sidebar.d.ts +4 -0
- package/dist/Components/layout/sidebar.d.ts.map +1 -1
- package/dist/Components/layout/splitView.d.ts +1 -1
- package/dist/Components/layout/splitView.d.ts.map +1 -1
- package/dist/Components/layout/tabpanel/tab.d.ts +1 -0
- package/dist/Components/layout/tabpanel/tab.d.ts.map +1 -1
- package/dist/Components/layout/tabpanel/tabline.d.ts +1 -2
- package/dist/Components/layout/tabpanel/tabline.d.ts.map +1 -1
- package/dist/Components/layout/tabpanel/tabpanel.d.ts +1 -1
- package/dist/Components/layout/tabpanel/tabpanel.d.ts.map +1 -1
- package/dist/Components/lineinputs/CheckableControl.d.ts +7 -0
- package/dist/Components/lineinputs/CheckableControl.d.ts.map +1 -0
- package/dist/Components/lineinputs/LabeledInputControl.d.ts +7 -0
- package/dist/Components/lineinputs/LabeledInputControl.d.ts.map +1 -0
- package/dist/Components/lineinputs/SelectControl.d.ts +7 -0
- package/dist/Components/lineinputs/SelectControl.d.ts.map +1 -0
- package/dist/Components/lineinputs/checkbox/Checkbox.d.ts +11 -5
- package/dist/Components/lineinputs/checkbox/Checkbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts +1 -2
- package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts +1 -2
- package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/dropdown.d.ts +7 -6
- package/dist/Components/lineinputs/dropdown.d.ts.map +1 -1
- package/dist/Components/lineinputs/label.d.ts +14 -0
- package/dist/Components/lineinputs/label.d.ts.map +1 -0
- package/dist/Components/lineinputs/radio.d.ts +20 -7
- package/dist/Components/lineinputs/radio.d.ts.map +1 -1
- package/dist/Components/lineinputs/textbox.d.ts +7 -6
- package/dist/Components/lineinputs/textbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/toggle.d.ts +10 -3
- package/dist/Components/lineinputs/toggle.d.ts.map +1 -1
- package/dist/Components/menu/button.d.ts +6 -3
- package/dist/Components/menu/button.d.ts.map +1 -1
- package/dist/Components/menu/toolbar.d.ts +1 -1
- package/dist/Components/menu/toolbar.d.ts.map +1 -1
- package/dist/Components/status/progressBar.d.ts +1 -1
- package/dist/Components/status/progressBar.d.ts.map +1 -1
- package/dist/Components/theme/stylesheetPicker.d.ts +6 -7
- package/dist/Components/theme/stylesheetPicker.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2539 -1839
- package/dist/index.js.map +1 -1
- package/dist/{jsx-dev-runtime-Kf1Dhu-y.js → jsx-dev-runtime-CRDpvXOH.js} +481 -472
- package/dist/jsx-dev-runtime-CRDpvXOH.js.map +1 -0
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/dist/routing/RouteLink.d.ts +1 -0
- package/dist/routing/RouteLink.d.ts.map +1 -1
- package/dist/routing/RouteQuery.d.ts +1 -0
- package/dist/routing/RouteQuery.d.ts.map +1 -1
- package/dist/routing/RouteScope.d.ts +1 -0
- package/dist/routing/RouteScope.d.ts.map +1 -1
- package/dist/routing/RouteValue.d.ts +1 -0
- package/dist/routing/RouteValue.d.ts.map +1 -1
- package/dist/runtime.d.ts +5 -13
- package/dist/runtime.d.ts.map +1 -1
- package/dist/styling/styleRegistry.d.ts +0 -4
- package/dist/styling/styleRegistry.d.ts.map +1 -1
- package/dist/styling/theme.d.ts +14 -2
- package/dist/styling/theme.d.ts.map +1 -1
- package/package.json +5 -6
- package/styles/structural.css +888 -398
- package/themes/README.md +30 -72
- package/themes/base.css +311 -0
- package/themes/java/theme.css +14 -423
- package/themes/minimal/theme.css +3 -419
- package/themes/shiny/theme.css +127 -508
- package/dist/jsx-dev-runtime-Kf1Dhu-y.js.map +0 -1
- package/dist/styling/baseStyleDefinitions.d.ts +0 -75
- package/dist/styling/baseStyleDefinitions.d.ts.map +0 -1
- package/themes/dark.css +0 -98
- package/themes/light.css +0 -98
package/README.md
CHANGED
|
@@ -27,10 +27,12 @@ Presentation should be equally direct. Native HTML already defines buttons,
|
|
|
27
27
|
inputs, tables, lists, progress, dialogs, and landmarks, so Fray uses those
|
|
28
28
|
elements when their semantics match. Components that need another boundary use
|
|
29
29
|
readable light-DOM host names rather than framework identity classes.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
Within a component host, Fray uses classes for owned parts, purely visual
|
|
31
|
+
states with no semantic equivalent, and meaningful reusable traits; those
|
|
32
|
+
classes do not identify components. Native and ARIA state attributes remain the
|
|
33
|
+
sole semantic state source and are targeted directly by CSS. Renderer markers,
|
|
34
|
+
diagnostics, interoperability, and actual data use `data-*`; Fray does not use
|
|
35
|
+
`data-*` as its routine component-CSS hook.
|
|
34
36
|
|
|
35
37
|
## Design model
|
|
36
38
|
|
|
@@ -92,15 +94,17 @@ Responsibility stays at the narrowest layer that understands it:
|
|
|
92
94
|
|
|
93
95
|
## Set up a browser application
|
|
94
96
|
|
|
95
|
-
Fray ships ESM, TypeScript declarations, automatic/classic JSX runtimes,
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
Fray ships ESM, TypeScript declarations, automatic/classic JSX runtimes,
|
|
98
|
+
variable defaults, replaceable theme treatments, and replaceable color
|
|
99
|
+
palettes. Glue is a peer dependency. Applications collect structural CSS from
|
|
100
|
+
the component classes they actually declare.
|
|
98
101
|
|
|
99
102
|
```ts
|
|
100
103
|
import {Emitter} from '@sylwellsoftware/glue'
|
|
101
104
|
import {
|
|
102
105
|
Button,
|
|
103
106
|
Component,
|
|
107
|
+
Header,
|
|
104
108
|
Panel,
|
|
105
109
|
Sidebar,
|
|
106
110
|
Textbox,
|
|
@@ -108,7 +112,7 @@ import {
|
|
|
108
112
|
createFrayRuntime,
|
|
109
113
|
h,
|
|
110
114
|
} from '@sylwellsoftware/fray'
|
|
111
|
-
import '@sylwellsoftware/fray/
|
|
115
|
+
import '@sylwellsoftware/fray/themes/base.css'
|
|
112
116
|
import '@sylwellsoftware/fray/colors/iceblue/colors.css'
|
|
113
117
|
import '@sylwellsoftware/fray/themes/minimal/theme.css'
|
|
114
118
|
|
|
@@ -118,7 +122,10 @@ class App extends Component {
|
|
|
118
122
|
static dependencies = [Button, Panel, Textbox, Toolbar]
|
|
119
123
|
|
|
120
124
|
render() {
|
|
121
|
-
return h(Panel, {
|
|
125
|
+
return h(Panel, {
|
|
126
|
+
className: 'fray-fill-horizontal fray-fill-vertical',
|
|
127
|
+
header: 'Profile',
|
|
128
|
+
},
|
|
122
129
|
h(Textbox, {label: 'Name', valueEmitter: name}),
|
|
123
130
|
h(Toolbar, {label: 'Profile actions'},
|
|
124
131
|
h(Button, {label: 'Save', onClick: () => save(name.get())})))
|
|
@@ -126,6 +133,7 @@ class App extends Component {
|
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
const runtime = createFrayRuntime()
|
|
136
|
+
runtime.registerStyles(App).injectStyles(document)
|
|
129
137
|
runtime.mount(runtime.create(App), document.querySelector('#app')!)
|
|
130
138
|
|
|
131
139
|
function save(value: string) {
|
|
@@ -133,6 +141,14 @@ function save(value: string) {
|
|
|
133
141
|
}
|
|
134
142
|
```
|
|
135
143
|
|
|
144
|
+
Root sizing is application-owned. Add `fray-fill-horizontal` to a rendered
|
|
145
|
+
application root to claim exactly `100vw`, `fray-fill-vertical` to claim
|
|
146
|
+
exactly `100vh`, or both for a fullscreen application. Each axis supplies its
|
|
147
|
+
own root overflow fallback and zero minimum; descendant islands are bounded
|
|
148
|
+
and scroll on only the opted-in axes. Other components retain their intrinsic
|
|
149
|
+
minimums and their own structural overflow contracts. A root with neither
|
|
150
|
+
modifier keeps its ordinary embedded/content-sized behavior.
|
|
151
|
+
|
|
136
152
|
The prebuilt structural file targets Fray's default `fray-` hosts. Applications
|
|
137
153
|
with custom components or configured host names may instead register their root
|
|
138
154
|
dependencies and call `runtime.injectStyles(document)`; collection remains
|
|
@@ -380,11 +396,11 @@ not a framework identity class. The default application runtime therefore
|
|
|
380
396
|
produces DOM such as:
|
|
381
397
|
|
|
382
398
|
```html
|
|
383
|
-
<fray-panel class="
|
|
384
|
-
<fray-textbox data-fray-component="textbox">
|
|
385
|
-
<input type="text">
|
|
399
|
+
<fray-panel data-fray class="island" data-fray-component="panel">
|
|
400
|
+
<fray-textbox data-fray data-fray-component="textbox">
|
|
401
|
+
<input data-fray type="text">
|
|
386
402
|
</fray-textbox>
|
|
387
|
-
<button data-fray-component="button">Save</button>
|
|
403
|
+
<button data-fray data-fray-component="button">Save</button>
|
|
388
404
|
</fray-panel>
|
|
389
405
|
```
|
|
390
406
|
|
|
@@ -393,48 +409,38 @@ Native semantics remain native: `Button` renders `button`, `Toggle` renders
|
|
|
393
409
|
`thead`/`th`. `Tab` is a declarative child consumed by `TabPanel` and has no
|
|
394
410
|
independent root. The
|
|
395
411
|
`data-fray-component` keeps diagnostics unambiguous. It is not a structural or
|
|
396
|
-
theme selector. Fray may merge public presentation traits such as `
|
|
412
|
+
theme selector. Fray may merge public presentation traits such as `island`
|
|
397
413
|
with an application-supplied `class`/`className`; those traits describe a
|
|
398
414
|
reusable capability, not component identity.
|
|
399
415
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
416
|
+
Pass `island` to a fixed-host component when that surface should be visually
|
|
417
|
+
separated from the page. Fray adds the reusable `island` class to its host;
|
|
418
|
+
applications may use the same class on deliberate native surface boundaries.
|
|
419
|
+
The component-owned rule consumes `--island-*` variables, so an island theme
|
|
420
|
+
can add gutters, an edge, and elevation while a flat theme can leave the
|
|
421
|
+
modifier visually neutral. Islands are one surface layer: nesting an `island`
|
|
422
|
+
component below another island is rejected. Application-authored native island
|
|
423
|
+
classes must follow the same no-nesting invariant. A surface island never
|
|
424
|
+
creates a nested theme or palette scope.
|
|
425
|
+
|
|
426
|
+
Built-in host names are fixed public DOM: Fray adds its one required custom
|
|
427
|
+
element hyphen and removes internal word separators from the component stem.
|
|
428
|
+
For example, `Panel`, `ListView`, and `ThemePicker` render as `<fray-panel>`,
|
|
429
|
+
`<fray-listview>`, and `<fray-themepicker>`. They cannot be prefixed, renamed,
|
|
430
|
+
or made prefix-free at runtime. This lets distributed stylesheets target hosts
|
|
431
|
+
directly and predictably.
|
|
432
|
+
|
|
433
|
+
Every element created by Fray's renderer also receives the boolean `data-fray`
|
|
434
|
+
attribute. Themes can therefore target native Fray output without affecting
|
|
435
|
+
other UI libraries: `button[data-fray]`, `input[data-fray]`, and
|
|
436
|
+
`dialog[data-fray]`. The marker is renderer-owned and cannot be removed through
|
|
437
|
+
component props. `data-fray-component` remains diagnostic metadata, not an
|
|
438
|
+
ordinary styling selector.
|
|
415
439
|
|
|
416
|
-
const selectedOverrides = createFrayRuntime({
|
|
417
|
-
elementNames: {
|
|
418
|
-
prefix: null,
|
|
419
|
-
overrides: {'panel': 'change-panel'},
|
|
420
|
-
},
|
|
421
|
-
})
|
|
422
|
-
// <change-panel>, with standalone names for the other components
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
HTML custom-element names must contain a hyphen, so prefixless mode uses each
|
|
426
|
-
component's standards-valid standalone name rather than invalid names such as
|
|
427
|
-
`<panel>` or `<listview>`. Prefixes and overrides must be lowercase kebab-case;
|
|
428
|
-
an exact override must itself be a non-reserved custom-element name.
|
|
429
|
-
|
|
430
|
-
Each runtime owns its element-name mapping and structural-style registry.
|
|
431
440
|
Create the runtime once at application startup, then create and mount the root
|
|
432
|
-
through that runtime.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
Styles still live in the document's global cascade because these hosts are
|
|
436
|
-
deliberately unregistered light-DOM elements, not Web Components or Shadow DOM
|
|
437
|
-
boundaries.
|
|
441
|
+
through that runtime. Styles live in the document's global cascade because the
|
|
442
|
+
hosts are deliberately unregistered light-DOM elements, not Web Components or
|
|
443
|
+
Shadow DOM boundaries.
|
|
438
444
|
|
|
439
445
|
## Reactive templates
|
|
440
446
|
|
|
@@ -449,8 +455,8 @@ canonical vnode operation, but it is not the built-in component authoring
|
|
|
449
455
|
format. The workspace lint gate rejects new `h()` templates under
|
|
450
456
|
`packages/fray/src/Components`.
|
|
451
457
|
|
|
452
|
-
|
|
453
|
-
|
|
458
|
+
Custom hosts are available inside a TSX component through its protected `Host`
|
|
459
|
+
template component:
|
|
454
460
|
|
|
455
461
|
```tsx
|
|
456
462
|
interface BadgeProps extends ComponentProps {
|
|
@@ -466,12 +472,11 @@ class Badge extends Component<BadgeProps> {
|
|
|
466
472
|
}
|
|
467
473
|
|
|
468
474
|
static override hostName = 'badge'
|
|
469
|
-
static override standaloneHostName = 'ui-badge'
|
|
470
475
|
}
|
|
471
476
|
```
|
|
472
477
|
|
|
473
|
-
At runtime that template produces `<fray-badge
|
|
474
|
-
|
|
478
|
+
At runtime that template produces `<fray-badge>`. `hostName` is a stable
|
|
479
|
+
lowercase kebab-case component identity; Fray derives the fixed host spelling.
|
|
475
480
|
Native-root components such as `Button` use their native tag directly in TSX
|
|
476
481
|
instead of `Host`.
|
|
477
482
|
|
|
@@ -481,7 +486,7 @@ Glue values have explicit behavior at each template boundary:
|
|
|
481
486
|
| --- | --- |
|
|
482
487
|
| `{emitter}` | Render the current value and patch only that child range on emission. |
|
|
483
488
|
| `<Child source={emitter} />` | Pass the emitter object unchanged; the child owns how it consumes it. |
|
|
484
|
-
| `prop={live(emitter)}` | Subscribe a
|
|
489
|
+
| `prop={live(emitter)}` | Subscribe a DOM property or a component-declared live prop one way to the emitter's current value. |
|
|
485
490
|
| `<input bind:value={emitter} />` | Bind a writable string emitter and native `value` two ways. |
|
|
486
491
|
| `<input bind:checked={emitter} />` | Bind a writable boolean emitter and native `checked` two ways. |
|
|
487
492
|
| `this.read(emitter)` | Read during `render()` and rerender the component while that dependency is used. |
|
|
@@ -494,6 +499,16 @@ The component still owns and disposes emitters it creates. Direct rendering
|
|
|
494
499
|
uses only an emitter's value; use `snapshot()` when loading and error state
|
|
495
500
|
must affect the markup.
|
|
496
501
|
|
|
502
|
+
Component props do not implicitly unwrap emitters. A raw emitter prop passes
|
|
503
|
+
the emitter object to the component, while `live(emitter)` passes its current
|
|
504
|
+
value and subscribes at the renderer boundary. Every class component has an
|
|
505
|
+
explicit live-prop contract and rejects `live()` outside its allowlist in both
|
|
506
|
+
typed templates and at runtime. `live()` is reserved for small render-time
|
|
507
|
+
state such as availability, validation, and busy/pressed state. Identity,
|
|
508
|
+
callbacks, initial values, structural collections, and emitter ownership are
|
|
509
|
+
ordinary props. A data prop documented as accepting a readable emitter is a
|
|
510
|
+
separate input-source contract, not a `live()` prop.
|
|
511
|
+
|
|
497
512
|
This complete example uses mutable and derived emitters, direct emitter
|
|
498
513
|
children, raw emitter props, native two-way bindings, a one-way live property,
|
|
499
514
|
conditional tracked state, and a `LiveQuery` passed to a child:
|
|
@@ -619,7 +634,7 @@ class ChangeApp extends Component {
|
|
|
619
634
|
static dependencies = [Button, Panel, Preview, Results, Textbox]
|
|
620
635
|
}
|
|
621
636
|
|
|
622
|
-
const runtime = createFrayRuntime(
|
|
637
|
+
const runtime = createFrayRuntime()
|
|
623
638
|
runtime.registerStyles(ChangeApp).injectStyles(document)
|
|
624
639
|
runtime.mount(runtime.create(ChangeApp), document.querySelector('#app')!)
|
|
625
640
|
```
|
|
@@ -690,8 +705,10 @@ or `defaultValue` in new code.
|
|
|
690
705
|
|
|
691
706
|
## Stable component reference
|
|
692
707
|
|
|
693
|
-
All components also accept `children`, `className` (`class` is an alias),
|
|
694
|
-
sibling-local `key` through the common
|
|
708
|
+
All components also accept `children`, `className` (`class` is an alias), an
|
|
709
|
+
`island` surface modifier, and a sibling-local `key` through the common
|
|
710
|
+
component props. The modifier is static presentation input, not a `live()`
|
|
711
|
+
property.
|
|
695
712
|
|
|
696
713
|
| Component | Important props | User callback | State/emitter behavior |
|
|
697
714
|
| --- | --- | --- | --- |
|
|
@@ -699,23 +716,55 @@ sibling-local `key` through the common component props.
|
|
|
699
716
|
| `Toolbar` | `label`, `orientation`, `id`, `children` | None | Stateless named toolbar; orientation is horizontal or vertical. |
|
|
700
717
|
| `Textbox` | `label` or `ariaLabel`, value props, `disabled`, `required`, `readOnly`, `error`, native text constraints, `inputRef` | `onInput(value, event)`, `onChange(value, event)` | String `valueEmitter`; external emitter changes patch the native input without replacing it. |
|
|
701
718
|
| `Dropdown<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required`, `error`, `placeholder`, `name` | `onChange(value, event)` | Typed string/number `valueEmitter`; `options` may be an array or readable emitter. |
|
|
702
|
-
| `RadioButton` | `label`, `name`, `value`, `checked`, `disabled`, `required` | `onChange(checked, event)` | Native radio input with a
|
|
703
|
-
| `RadioGroup<T>` | `options`, `label` or `ariaLabel`, value props, `name`, `disabled`, `required` | `onChange(value, event)` | Native radio inputs with one selected `valueEmitter`
|
|
704
|
-
| `Toggle<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required` | `onChange(value, event)` | One selected value;
|
|
705
|
-
| `Checkbox<T>` | `symbols`, `label`, value props, `disabled`, `required`, `name` | `onChange(value, event)` | Two-state semantic value by default;
|
|
719
|
+
| `RadioButton` | `label`, `name`, `value`, `checked`, `disabled`, `required`, `error` | `onChange(checked, event)` | Native radio input with a labelled fixed shell; `checked`, `disabled`, `required`, and `error` support `live()`. |
|
|
720
|
+
| `RadioGroup<T>` | Plain-array `options`, `label` or `ariaLabel`, value props, `name`, `disabled`, `required`, `error` | `onChange(value, event)` | Native radio inputs with one selected `valueEmitter`; `disabled`, `required`, and `error` support `live()`. |
|
|
721
|
+
| `Toggle<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required`, `error` | `onChange(value, event)` | One selected value; `disabled`, `required`, and `error` support `live()`. |
|
|
722
|
+
| `Checkbox<T>` | `symbols`, `label`, value props, `disabled`, `required`, `error`, `name` | `onChange(value, event)` | Two-state semantic value by default; `disabled`, `required`, and `error` support `live()`. |
|
|
706
723
|
| `TriCheckbox` | Checkbox props except `symbols` | `onChange(value, event)` | Cycles deny → neutral → prefer using `FilterMode`. |
|
|
707
724
|
| `QuadCheckbox` | Checkbox props except `symbols` | `onChange(value, event)` | Cycles deny → neutral → prefer → require using `FilterMode`. |
|
|
708
|
-
| `
|
|
709
|
-
| `
|
|
710
|
-
| `
|
|
725
|
+
| `Header` | `id`, `headingId`, `level`, `children` | None | Styled heading surface using a native `h1`–`h6`; level defaults to `2`. |
|
|
726
|
+
| `Panel` | `header`, `toolbar`, `orientation`, `disabled`, `id`, `children` | None | Stateless labelled section that composes Header when header content exists; `disabled` describes the region but does not mutate descendant controls. |
|
|
727
|
+
| `Sidebar` | `header`, `toolbar`, `ariaLabel`, `id`, `children` | None | Fixed `fray-sidebar` host containing a native complementary region with fixed header/toolbar parts and independently scrolling content. |
|
|
728
|
+
| `SplitView` | `primary`, `secondary`, `direction`, `primarySize`, pane labels | None | Stateless, non-resizable two-pane flex layout with explicit overflow ownership and keyboard-focusable panes; `primarySize` is a flex-basis value. |
|
|
711
729
|
| `DescriptionList` / `DescriptionItem` | list `label`; item `term`, `value` or children | None | Native `dl`/`dt`/`dd` record summary with responsive term/value wrapping. |
|
|
712
|
-
| `ProgressBar` | `label`, `value` or `valueEmitter`, `max`, `valueText` | None |
|
|
730
|
+
| `ProgressBar` | `label`, `value` or `valueEmitter`, `max`, `valueText` | None | Native progress semantics with a clipped-label visual surface; a null value is indeterminate. |
|
|
713
731
|
| `ThemePicker` | `label` or `ariaLabel`, theme `options`, value props, `targetDocument`, `disabled` | `onChange(value, option, event)` | String `valueEmitter`; replaces only the theme stylesheet link. |
|
|
714
732
|
| `ColorPicker` | `label` or `ariaLabel`, color `options`, value props, `targetDocument`, `disabled` | `onChange(value, option, event)` | String `valueEmitter`; replaces only the color stylesheet link. |
|
|
715
733
|
| `Tab` | `id`, `label`, `disabled`, optional literal `route`, `children` | None | Declarative content marker consumed by `TabPanel`; a route annotation binds tab activation to the current route scope. |
|
|
716
734
|
| `TabLine` | `tabs`, `label`, `baseId`, value props | `onChange(id, event)` | Active-tab `valueEmitter`; arrow keys skip disabled tabs, with Home/End support. |
|
|
717
735
|
| `TabPanel` | `tabs` or `Tab` children, `label`, `id`, value props | `onChange(id, event)` | Owns or consumes the active-tab emitter, wires the selected tabpanel, and contextually registers annotated tabs when a router is present. |
|
|
718
736
|
|
|
737
|
+
### Live binding and data-source contracts
|
|
738
|
+
|
|
739
|
+
Use `live()` only for the props listed here. All other component props reject
|
|
740
|
+
`live()` in TSX, `h()`, and at runtime. Form-control errors render an alert,
|
|
741
|
+
set invalid state, and associate the control or group with that message.
|
|
742
|
+
|
|
743
|
+
| Component | `live()` props | Dedicated reactive input/state props |
|
|
744
|
+
| --- | --- | --- |
|
|
745
|
+
| `Button` | `disabled`, `pressed`, `busy` | None |
|
|
746
|
+
| `Textbox` | `disabled`, `required`, `readOnly`, `error` | `valueEmitter` |
|
|
747
|
+
| `Dropdown` | `disabled`, `required`, `error` | `options` may be an array or readable emitter; `valueEmitter` |
|
|
748
|
+
| `RadioButton` | `checked`, `disabled`, `required`, `error` | None |
|
|
749
|
+
| `RadioGroup` | `disabled`, `required`, `error` | `valueEmitter`; `options` is always an ordinary array |
|
|
750
|
+
| `Toggle` | `disabled`, `required`, `error` | `valueEmitter`; `options` is always an ordinary array |
|
|
751
|
+
| `Checkbox`, `TriCheckbox`, `QuadCheckbox` | `disabled`, `required`, `error` | `valueEmitter` |
|
|
752
|
+
| `ThemePicker`, `ColorPicker` | `disabled` | `valueEmitter` |
|
|
753
|
+
| `Dialog` | `showCloseButton` | `valueEmitter` controls open state |
|
|
754
|
+
| `Panel` | `disabled` | None |
|
|
755
|
+
| `ProgressBar` | None | `valueEmitter` |
|
|
756
|
+
| `ListView` | None | `items` may be an array or readable emitter; selection emitters are outputs |
|
|
757
|
+
| `TreeView` | None | `nodes` may be an array or readable emitter; selected/expanded emitters are outputs |
|
|
758
|
+
| `DataTable` | None | `data` may be an array or readable emitter; `dataSource`/`rest` and selection emitters are explicit source/state contracts |
|
|
759
|
+
| `FilterPanel` | None | `options` may be an array or readable emitter |
|
|
760
|
+
| Layout, tab, description, toolbar, placeholder, and routing components | None | Their ordinary structural/configuration props require an owner rerender when changed |
|
|
761
|
+
|
|
762
|
+
Readable data sources carry their own fetch state and error through Glue's
|
|
763
|
+
`getFetchState()` and `getError()` APIs. `ListView`, `DataTable`, `FilterPanel`,
|
|
764
|
+
and `TreeView` surface those source failures as data-loading errors. This is
|
|
765
|
+
separate from a control's `error` prop, which represents validation or other
|
|
766
|
+
application-level input feedback.
|
|
767
|
+
|
|
719
768
|
Invalid option arrays, duplicate tab IDs, unsupported orientations, malformed
|
|
720
769
|
emitters, and non-function callbacks fail with descriptive errors.
|
|
721
770
|
|
|
@@ -770,6 +819,80 @@ h(Toggle, {
|
|
|
770
819
|
})
|
|
771
820
|
```
|
|
772
821
|
|
|
822
|
+
### RadioGroup
|
|
823
|
+
|
|
824
|
+
`RadioGroup` deliberately has a narrow reactive contract. Its selectable
|
|
825
|
+
`options` are ordinary structural input, its `valueEmitter` is a stable raw
|
|
826
|
+
writable state channel, and only `disabled`, `required`, and `error` accept one-way
|
|
827
|
+
`live()` bindings.
|
|
828
|
+
|
|
829
|
+
| Prop | Role | `live()` support |
|
|
830
|
+
| --- | --- | --- |
|
|
831
|
+
| `options` | Selectable value/label tuples supplied by the owner | No |
|
|
832
|
+
| `disabled` | Current availability state | Yes, with a readable boolean emitter |
|
|
833
|
+
| `required` | Current form-requirement state | Yes, with a readable boolean emitter |
|
|
834
|
+
| `error` | Current validation message and invalid state | Yes, with a readable error emitter |
|
|
835
|
+
| `valueEmitter` | Stable writable selected-value channel | Pass the emitter raw; do not wrap it |
|
|
836
|
+
| `value`, `defaultValue`, `initialValue` | Initial value when no `valueEmitter` is supplied | No |
|
|
837
|
+
| `id`, `label`, `ariaLabel`, `name` | Ordinary identity and presentation input | No `live()` binding |
|
|
838
|
+
| `onChange` | Action callback | No |
|
|
839
|
+
|
|
840
|
+
Static and live availability state can be combined without changing the option
|
|
841
|
+
contract:
|
|
842
|
+
|
|
843
|
+
```tsx
|
|
844
|
+
const selectedView = new Emitter<'list' | 'grid'>('list')
|
|
845
|
+
const unavailable = new Emitter(false)
|
|
846
|
+
const mustChoose = new Emitter(true)
|
|
847
|
+
|
|
848
|
+
<RadioGroup
|
|
849
|
+
label="View"
|
|
850
|
+
options={[
|
|
851
|
+
['list', 'List'],
|
|
852
|
+
['grid', 'Grid'],
|
|
853
|
+
]}
|
|
854
|
+
valueEmitter={selectedView}
|
|
855
|
+
disabled={live(unavailable)}
|
|
856
|
+
required={live(mustChoose)}
|
|
857
|
+
/>
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
`live(optionsEmitter)` and a raw options emitter are both unsupported. When an
|
|
861
|
+
application genuinely owns a changing option vocabulary, its owning class
|
|
862
|
+
component must make that structural rerender explicit:
|
|
863
|
+
|
|
864
|
+
```tsx
|
|
865
|
+
interface ViewChooserProps extends ComponentProps {
|
|
866
|
+
options: ReadableEmitter<readonly RadioOption[]>
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
class ViewChooser extends Component<ViewChooserProps> {
|
|
870
|
+
render() {
|
|
871
|
+
return <RadioGroup
|
|
872
|
+
label="View"
|
|
873
|
+
options={this.read(this.props.options)}
|
|
874
|
+
/>
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
Calling `optionsEmitter.get()` directly in `render()` only reads a snapshot and
|
|
880
|
+
does not subscribe. `this.read(optionsEmitter)` rerenders the owner when the
|
|
881
|
+
array changes; normal vnode reconciliation then supplies the new ordinary
|
|
882
|
+
array prop to `RadioGroup`. The owner remains responsible for deciding what an
|
|
883
|
+
option removal means for its selected-value emitter. Fray does not silently
|
|
884
|
+
select, clear, or otherwise rewrite that state.
|
|
885
|
+
|
|
886
|
+
`label` is a `FrayChild`, so it may still contain an emitter that is rendered
|
|
887
|
+
and subscribed as child content. That fine-grained child behavior is distinct
|
|
888
|
+
from making the `label` property itself a `live()` binding. The same is true of
|
|
889
|
+
an individual `RadioOption` label: its rendered content may be reactive without
|
|
890
|
+
making the option array or its selectable values live.
|
|
891
|
+
|
|
892
|
+
Because `live()` subscriptions belong to a parent render record, pass live
|
|
893
|
+
props through JSX or `h()`. Direct `new RadioGroup(...)` construction accepts
|
|
894
|
+
resolved booleans and ordinary arrays only.
|
|
895
|
+
|
|
773
896
|
### Checkbox variants
|
|
774
897
|
|
|
775
898
|
```ts
|
|
@@ -781,6 +904,18 @@ h(QuadCheckbox, {label: 'Required tags', defaultValue: FilterMode.Require})
|
|
|
781
904
|
`FilterMode` values are `Deny`, `Neutral`, `Prefer`, and `Require`. The basic
|
|
782
905
|
checkbox uses neutral/prefer, while the variants expose the additional states.
|
|
783
906
|
|
|
907
|
+
### Header
|
|
908
|
+
|
|
909
|
+
```tsx
|
|
910
|
+
<Header id="portfolio-header" headingId="portfolio-title" level={2}>
|
|
911
|
+
Portfolio summary
|
|
912
|
+
</Header>
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
`level` is an integer from `1` through `6` and defaults to `2`. Header children
|
|
916
|
+
must be valid native heading content. The custom host owns presentation while
|
|
917
|
+
the nested native heading owns document semantics.
|
|
918
|
+
|
|
784
919
|
### Panel
|
|
785
920
|
|
|
786
921
|
```ts
|
|
@@ -803,10 +938,10 @@ h(Sidebar, {
|
|
|
803
938
|
}, h('ul', null, h('li', null, 'Release automation')))
|
|
804
939
|
```
|
|
805
940
|
|
|
806
|
-
The surrounding grid or flex layout must bound the Sidebar's height. Its
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
`header`.
|
|
941
|
+
The surrounding grid or flex layout must bound the Sidebar's height. Its native
|
|
942
|
+
complementary region is inside the fixed Sidebar host; Header and toolbar remain
|
|
943
|
+
fixed while the dedicated content part owns vertical scrolling and is keyboard-
|
|
944
|
+
focusable. Supply `ariaLabel` when there is no visible `header`.
|
|
810
945
|
|
|
811
946
|
### Declarative tabs
|
|
812
947
|
|
|
@@ -837,149 +972,74 @@ content and IDs.
|
|
|
837
972
|
|
|
838
973
|
## Styling and accessibility
|
|
839
974
|
|
|
840
|
-
Fray
|
|
841
|
-
|
|
842
|
-
|
|
|
843
|
-
| --- | --- | --- |
|
|
844
|
-
|
|
|
845
|
-
|
|
|
846
|
-
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
and
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
CSS custom properties are Fray's primary theme integration protocol:
|
|
975
|
+
A Fray application loads four ordered styling inputs:
|
|
976
|
+
|
|
977
|
+
| Order | Input | Responsibility |
|
|
978
|
+
| --- | --- | --- |
|
|
979
|
+
| 1 | `themes/base.css` | Default palette anchors, palette-ramp derivation, and semantic custom-property defaults only |
|
|
980
|
+
| 2 | Runtime-collected component CSS | Rules from the `static css` declarations of the component classes the application declares |
|
|
981
|
+
| 3 | `colors/<name>/colors.css` | Palette anchors and endpoints only |
|
|
982
|
+
| 4 | `themes/<name>/theme.css` | Intentional custom-property overrides; `color-scheme` is the only ordinary declaration allowed |
|
|
983
|
+
|
|
984
|
+
Application layout remains in application-owned CSS and is not a fifth Fray
|
|
985
|
+
layer. Named color and theme files never import the base file; load order is
|
|
986
|
+
explicit at the application entry point.
|
|
987
|
+
|
|
988
|
+
Component classes own every selector and every declaration that consumes a
|
|
989
|
+
Fray variable. Shared rules live in meaningful abstract component classes when
|
|
990
|
+
those components share DOM or behavior. For example, `Dropdown` inherits the
|
|
991
|
+
labeled-input contract and then the select-shell contract. The collector walks
|
|
992
|
+
that class chain base-to-derived, resolves each class's own `&` selectors
|
|
993
|
+
against the concrete host, and then traverses dependencies declared at every
|
|
994
|
+
level. The older named `baseStyles` recipe system has been removed.
|
|
995
|
+
|
|
996
|
+
A component's `static css` should contain the absolute minimum needed for its
|
|
997
|
+
contract. Every selector, declaration, pseudo-element, and variable must have a
|
|
998
|
+
specific purpose that cannot be expressed more simply. A base-class edit must
|
|
999
|
+
be reviewed against all descendants because its CSS is compiled into each
|
|
1000
|
+
concrete descendant that is used.
|
|
1001
|
+
|
|
1002
|
+
The checked-in `styles/structural.css` is a complete generated artifact for
|
|
1003
|
+
non-tree-shaken/diagnostic use. Normal applications should register their root
|
|
1004
|
+
component and inject the resulting usage-specific stylesheet:
|
|
871
1005
|
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
▼
|
|
877
|
-
theme.css
|
|
878
|
-
global UI roles (font, spacing, shape, surface)
|
|
879
|
-
│
|
|
880
|
-
▼
|
|
881
|
-
generic families (header, button, input, panel, selection)
|
|
882
|
-
│
|
|
883
|
-
▼
|
|
884
|
-
optional variants (table header, tab button, toggle button,
|
|
885
|
-
dropdown trigger, dialog header)
|
|
886
|
-
│
|
|
887
|
-
▼
|
|
888
|
-
structural CSS and custom components
|
|
1006
|
+
```ts
|
|
1007
|
+
const runtime = createFrayRuntime()
|
|
1008
|
+
runtime.registerStyles(App).injectStyles(document)
|
|
1009
|
+
runtime.mount(runtime.create(App), root)
|
|
889
1010
|
```
|
|
890
1011
|
|
|
891
|
-
|
|
892
|
-
toward its generic family. A theme can therefore change all header-like or
|
|
893
|
-
button-like elements with a few assignments, then override only the variants
|
|
894
|
-
that should look different. `frayThemeVariableCatalog` exports this contract in
|
|
895
|
-
machine-readable form, including every variable's layer, family, value kind,
|
|
896
|
-
purpose, and optional fallback.
|
|
1012
|
+
### Custom properties
|
|
897
1013
|
|
|
898
|
-
|
|
899
|
-
|
|
1014
|
+
`base.css` derives full primary, secondary, and neutral ramps from each
|
|
1015
|
+
palette's `500` anchor plus light/dark mix endpoints. A color file therefore
|
|
1016
|
+
sets anchors and endpoints, while a theme maps or overrides semantic families
|
|
1017
|
+
such as `--button-*`, `--input-*`, `--panel-*`, and `--selection-*`.
|
|
1018
|
+
`frayThemeVariableCatalog` exposes the supported vocabulary.
|
|
900
1019
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
background: var(--table-header-background, var(--header-background));
|
|
905
|
-
}
|
|
906
|
-
```
|
|
1020
|
+
`--application-background` owns the canvas behind an axis-filling application
|
|
1021
|
+
root and its islands. Its base value is the white palette endpoint, which
|
|
1022
|
+
Shiny and Minimal both retain.
|
|
907
1023
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
introduced solely to carry a split trait.
|
|
915
|
-
|
|
916
|
-
Themes directly target native elements, public traits, native pseudo-parts,
|
|
917
|
-
and native/ARIA state. They never target `data-fray-component` or `data-part`.
|
|
918
|
-
Every theme seeds inherited variables with a zero-specificity
|
|
919
|
-
`:root`/`[data-theme]` boundary rule. Presentation selectors use `@scope` with
|
|
920
|
-
nested theme roots and `[data-theme-exclude]` limits, a named cascade layer,
|
|
921
|
-
and low-specificity `:where()` selectors. Shiny's highlights and
|
|
922
|
-
select/progress decoration follow that contract and yield to native
|
|
923
|
-
representation under forced colors.
|
|
924
|
-
|
|
925
|
-
`coloredlike` defaults to the primary palette. Components can provide
|
|
926
|
-
`--colored-base`, `--colored-light`, `--colored-dark`, and
|
|
927
|
-
`--colored-contrast`; the active theme decides whether those inputs become a
|
|
928
|
-
flat color, gradient, other polish, or no special treatment.
|
|
1024
|
+
The explicit `island` modifier consumes `--island-margin`,
|
|
1025
|
+
`--island-padding`, `--island-background`, `--island-border`,
|
|
1026
|
+
`--island-radius`, and `--island-shadow`. Base and Minimal keep its layout and
|
|
1027
|
+
elevation neutral; Shiny uses it for Bank2-style separated surfaces. Themes do
|
|
1028
|
+
not infer island boundaries from component type or nesting, and islands cannot
|
|
1029
|
+
contain other islands.
|
|
929
1030
|
|
|
930
1031
|
### Runtime selection
|
|
931
1032
|
|
|
932
1033
|
`replaceFrayStylesheet` maintains one
|
|
933
1034
|
`link[data-fray-stylesheet="theme"]` and one
|
|
934
1035
|
`link[data-fray-stylesheet="colors"]`. Replacing either link also sets the
|
|
935
|
-
corresponding
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
The default option catalogs resolve URLs against Fray's published package
|
|
940
|
-
layout for direct ESM/CDN use. A bundled application should ask its bundler to
|
|
941
|
-
emit each selectable CSS file as an asset and supply those resulting URLs:
|
|
942
|
-
|
|
943
|
-
```tsx
|
|
944
|
-
import {ColorPicker, Component, ThemePicker} from '@sylwellsoftware/fray'
|
|
945
|
-
import iceblueHref from '@sylwellsoftware/fray/colors/iceblue/colors.css?url'
|
|
946
|
-
import purpleHref from '@sylwellsoftware/fray/colors/purple/colors.css?url'
|
|
947
|
-
import minimalHref from '@sylwellsoftware/fray/themes/minimal/theme.css?url'
|
|
948
|
-
import shinyHref from '@sylwellsoftware/fray/themes/shiny/theme.css?url'
|
|
949
|
-
|
|
950
|
-
const themes = [
|
|
951
|
-
{value: 'shiny', label: 'Shiny', href: shinyHref},
|
|
952
|
-
{value: 'minimal', label: 'Minimal', href: minimalHref},
|
|
953
|
-
]
|
|
954
|
-
const colors = [
|
|
955
|
-
{value: 'iceblue', label: 'Ice blue', href: iceblueHref},
|
|
956
|
-
{value: 'purple', label: 'Purple', href: purpleHref},
|
|
957
|
-
]
|
|
958
|
-
|
|
959
|
-
class AppearanceControls extends Component {
|
|
960
|
-
render() {
|
|
961
|
-
return <aside aria-label="Appearance">
|
|
962
|
-
<ThemePicker label="Theme" options={themes} defaultValue="shiny" />
|
|
963
|
-
<ColorPicker label="Colors" options={colors} defaultValue="iceblue" />
|
|
964
|
-
</aside>
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
static dependencies = [ColorPicker, ThemePicker]
|
|
968
|
-
}
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
The `?url` syntax above is supported by Vite; use the equivalent emitted-asset
|
|
972
|
-
mechanism for another bundler. The structural stylesheet is not replaced.
|
|
973
|
-
|
|
974
|
-
See [`themes/README.md`](themes/README.md) for the complete architecture and
|
|
975
|
-
variable families, and [`colors/README.md`](colors/README.md) for the palette
|
|
976
|
-
contract.
|
|
1036
|
+
corresponding root data attribute. `ThemePicker` and `ColorPicker` expose the
|
|
1037
|
+
same operation through the normal value-control contract. The base file and
|
|
1038
|
+
runtime-injected structural stylesheet stay loaded while those two links are
|
|
1039
|
+
replaced.
|
|
977
1040
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
operation, labelled roles, reduced motion, 200% configured text sizing, and
|
|
981
|
-
forced-colors focus visibility. A formal manual screen-reader pass is still a
|
|
982
|
-
release-candidate requirement; automated checks are not a substitute for it.
|
|
1041
|
+
See [the theme contract](themes/README.md) and
|
|
1042
|
+
[the palette contract](colors/README.md) for authoring details.
|
|
983
1043
|
|
|
984
1044
|
## Data workflows
|
|
985
1045
|
|
|
@@ -1043,8 +1103,8 @@ For a tree derived from domain state, a callback updates that real source; the
|
|
|
1043
1103
|
tree derivation then rebuilds and `deriveTreeNode` resolves the fresh node.
|
|
1044
1104
|
`updateTreeNode` is the equivalent pure path-copy operation.
|
|
1045
1105
|
|
|
1046
|
-
Generic `FilterState` keeps `neutral`, `prefer`, `require`, and `deny`
|
|
1047
|
-
values separate from glyphs and transport. `filterByState`,
|
|
1106
|
+
Generic `FilterState` keeps `neutral`, `prefer`, `require`, and `deny`
|
|
1107
|
+
semantic values separate from glyphs and transport. `filterByState`,
|
|
1048
1108
|
`deriveFilterPredicate`, and `deriveFilteredItems` consume caller-supplied
|
|
1049
1109
|
dimension matchers. `serializeFilterState`/`parseFilterState` round-trip
|
|
1050
1110
|
validated version-1 plain data without owning URL or storage access. Unknown
|
|
@@ -1063,5 +1123,5 @@ React/Vue adapters, a broad design system, a virtualized production data grid,
|
|
|
1063
1123
|
legacy-browser support, or a stable `1.0` API. See the [workspace
|
|
1064
1124
|
overview](../../README.md), [API surface](../../docs/API_SURFACE.md),
|
|
1065
1125
|
[architecture overview](../../docs/architecture.md),
|
|
1066
|
-
[changelog](
|
|
1126
|
+
[changelog](CHANGELOG.md), [contribution guide](../../CONTRIBUTING.md),
|
|
1067
1127
|
and [security policy](../../SECURITY.md).
|