@softov/scena 0.1.2 → 0.1.3
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/dist/core/builtins.d.ts +3 -0
- package/dist/{runtime → core}/builtins.js +1 -1
- package/dist/core/controls/command.d.ts +18 -0
- package/dist/{controls → core/controls}/command.js +2 -2
- package/dist/{controls → core/controls}/events.d.ts +1 -1
- package/dist/{controls → core/controls}/events.js +1 -1
- package/dist/core/controls/keybinding.d.ts +9 -0
- package/dist/{controls → core/controls}/keybinding.js +1 -1
- package/dist/core/controls/layout.d.ts +31 -0
- package/dist/{controls → core/controls}/layout.js +24 -7
- package/dist/core/controls/manifest.d.ts +21 -0
- package/dist/{controls → core/controls}/manifest.js +2 -2
- package/dist/core/controls/session.d.ts +16 -0
- package/dist/{controls → core/controls}/session.js +5 -13
- package/dist/{controls → core/controls}/shell.d.ts +2 -2
- package/dist/{controls → core/controls}/shell.js +1 -1
- package/dist/{controls → core/controls}/storage/layout-local.d.ts +1 -1
- package/dist/{controls → core/controls}/storage/session-local.d.ts +1 -1
- package/dist/{controls → core/controls}/when.d.ts +2 -2
- package/dist/{controls → core/controls}/when.js +1 -1
- package/dist/{converters → core/converters}/a2ui-v0.10.d.ts +1 -1
- package/dist/core/{component-graph.d.ts → graph/component-graph.d.ts} +1 -1
- package/dist/core/{component-graph.js → graph/component-graph.js} +1 -1
- package/dist/core/{mount-menu.d.ts → graph/mount-menu.d.ts} +1 -1
- package/dist/core/{mount-menu.js → graph/mount-menu.js} +1 -1
- package/dist/core/graph/mount-surface.d.ts +15 -0
- package/dist/core/{mount-surface.js → graph/mount-surface.js} +23 -4
- package/dist/core/{surface-presentation.d.ts → graph/surface-presentation.d.ts} +3 -3
- package/dist/{i18n → core/i18n}/i18n-backend.d.ts +1 -1
- package/dist/core/index.d.ts +34 -43
- package/dist/core/index.js +35 -38
- package/dist/core/{component-registry.d.ts → registry/component-registry.d.ts} +2 -2
- package/dist/core/{component-registry.js → registry/component-registry.js} +1 -1
- package/dist/core/registry/converter-registry.d.ts +2 -0
- package/dist/core/{converter-registry.js → registry/converter-registry.js} +1 -1
- package/dist/core/resolve/binding-resolver.d.ts +7 -0
- package/dist/core/{binding-resolver.js → resolve/binding-resolver.js} +2 -2
- package/dist/core/{dynamic-resolver.d.ts → resolve/dynamic-resolver.d.ts} +2 -2
- package/dist/core/{dynamic-resolver.js → resolve/dynamic-resolver.js} +1 -1
- package/dist/core/{path-resolver.d.ts → resolve/path-resolver.d.ts} +2 -2
- package/dist/core/{permissions.d.ts → resolve/permissions.d.ts} +1 -1
- package/dist/core/scena.d.ts +1 -1
- package/dist/core/scena.js +17 -16
- package/dist/core/{backends → store/backends}/modus-backend.d.ts +1 -1
- package/dist/core/{backends → store/backends}/storage-backend.d.ts +1 -1
- package/dist/core/{backends → store/backends}/yjs-backend.d.ts +1 -1
- package/dist/core/{reactive-store.d.ts → store/reactive-store.d.ts} +4 -4
- package/dist/core/{reactive-store.js → store/reactive-store.js} +2 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/porta/Limen.d.ts +1 -0
- package/dist/porta/Limen.js +1 -0
- package/dist/porta/PortaLock.d.ts +1 -0
- package/dist/porta/PortaLock.js +6 -2
- package/dist/porta/SigillumGate.d.ts +1 -0
- package/dist/porta/SigillumGate.js +1 -0
- package/dist/porta/commands.d.ts +1 -1
- package/dist/porta/login-form.block.d.ts +1 -1
- package/dist/porta/provider.d.ts +1 -1
- package/dist/porta/register-blocks.d.ts +2 -2
- package/dist/porta/register-blocks.js +4 -7
- package/dist/porta/register.d.ts +1 -1
- package/dist/porta/sigillum.d.ts +2 -2
- package/dist/react/DefaultShell.d.ts +47 -1
- package/dist/react/DefaultShell.js +81 -12
- package/dist/react/Scena.d.ts +2 -2
- package/dist/react/ScenaProvider.d.ts +1 -1
- package/dist/react/ShellSplitter.d.ts +1 -1
- package/dist/react/SurfaceArea.d.ts +5 -3
- package/dist/react/SurfaceArea.js +2 -2
- package/dist/react/ViewMount.d.ts +1 -1
- package/dist/react/ViewMount.js +4 -4
- package/dist/react/hooks/useI18n.d.ts +1 -1
- package/dist/react/hooks/useI18n.js +1 -1
- package/dist/react/hooks/useLabel.d.ts +1 -1
- package/dist/react/hooks/useLabel.js +1 -1
- package/dist/react/hooks/useLayout.d.ts +1 -1
- package/dist/react/hooks/useMounts.d.ts +1 -1
- package/dist/react/hooks/useStore.d.ts +1 -1
- package/dist/react/hooks/useStore.js +2 -2
- package/dist/react/hooks/useStoreLabel.d.ts +2 -2
- package/dist/react/hooks/useStoreLabel.js +1 -1
- package/dist/react/hooks/useSurfacePresentation.d.ts +3 -3
- package/dist/react/hooks/useSurfacePresentation.js +1 -1
- package/dist/react/mount-context.d.ts +1 -1
- package/dist/react/useSurfaceResize.d.ts +2 -2
- package/dist/runtime/client-handlers.d.ts +1 -1
- package/dist/runtime/in-memory-socket.d.ts +2 -2
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/layout-commands.d.ts +3 -3
- package/dist/runtime/layout-commands.js +2 -2
- package/dist/runtime/opener-commands.d.ts +5 -5
- package/dist/runtime/opener-commands.js +3 -3
- package/dist/runtime/surface-bridge.d.ts +5 -5
- package/dist/runtime/surface-bridge.js +1 -1
- package/dist/{runtime → sdk}/args-schema.d.ts +1 -1
- package/dist/{core → sdk}/disposable.d.ts +3 -1
- package/dist/{types → sdk}/index.d.ts +1 -0
- package/dist/{types → sdk}/index.js +1 -0
- package/dist/{types → sdk}/label.d.ts +1 -0
- package/dist/{core → sdk}/label.js +0 -1
- package/dist/{types → sdk}/layout.d.ts +25 -0
- package/dist/{types → sdk}/mount-surface.d.ts +19 -1
- package/dist/{types → sdk}/scena.d.ts +2 -1
- package/dist/styles/bar.css +8 -4
- package/dist/styles/base.css +18 -16
- package/dist/styles/controller.d.ts +27 -0
- package/dist/styles/controller.js +74 -0
- package/dist/styles/form.css +10 -0
- package/dist/styles/index.d.ts +2 -0
- package/dist/styles/index.js +3 -0
- package/dist/styles/surface.css +69 -3
- package/dist/styles/themes/tokens.css +1 -1
- package/dist/ui/_utils.d.ts +1 -1
- package/dist/ui/_utils.js +2 -2
- package/dist/ui/campus/CampusMappa.js +1 -1
- package/dist/ui/chart/catalog.d.ts +1 -1
- package/dist/ui/control/ButtonBar.css +61 -0
- package/dist/ui/control/ButtonBar.d.ts +36 -0
- package/dist/ui/control/ButtonBar.js +42 -0
- package/dist/ui/control/ThemeModeToggle.css +22 -0
- package/dist/ui/control/ThemeModeToggle.d.ts +20 -0
- package/dist/ui/control/ThemeModeToggle.js +34 -0
- package/dist/ui/control/ThemePicker.css +24 -0
- package/dist/ui/control/ThemePicker.d.ts +27 -0
- package/dist/ui/control/ThemePicker.js +33 -0
- package/dist/ui/control/catalog.d.ts +1 -1
- package/dist/ui/control/catalog.js +5 -1
- package/dist/ui/control/index.d.ts +6 -0
- package/dist/ui/control/index.js +3 -0
- package/dist/ui/data/Filter.d.ts +1 -1
- package/dist/ui/data/Listable.d.ts +3 -2
- package/dist/ui/data/Listable.js +4 -2
- package/dist/ui/data/Pagination.d.ts +4 -1
- package/dist/ui/data/Pagination.js +37 -9
- package/dist/ui/data/Tree.d.ts +2 -2
- package/dist/ui/data/Tree.js +1 -1
- package/dist/ui/data/catalog.d.ts +1 -1
- package/dist/ui/display/Alert.css +15 -10
- package/dist/ui/display/Badge.css +22 -14
- package/dist/ui/display/Badge.d.ts +1 -1
- package/dist/ui/display/Badge.js +1 -1
- package/dist/ui/display/DetailHeader.css +3 -3
- package/dist/ui/display/DetailList.js +3 -2
- package/dist/ui/display/DetailNotFound.d.ts +1 -1
- package/dist/ui/display/DetailNotFound.js +1 -1
- package/dist/ui/display/Progress.d.ts +1 -1
- package/dist/ui/display/Progress.js +1 -1
- package/dist/ui/display/Spinner.d.ts +1 -1
- package/dist/ui/display/Spinner.js +1 -1
- package/dist/ui/display/StatusDot.css +46 -0
- package/dist/ui/display/StatusDot.d.ts +17 -0
- package/dist/ui/display/StatusDot.js +16 -0
- package/dist/ui/display/Svg.d.ts +1 -1
- package/dist/ui/display/Svg.js +1 -1
- package/dist/ui/display/catalog.d.ts +1 -1
- package/dist/ui/display/catalog.js +1 -0
- package/dist/ui/display/index.d.ts +2 -0
- package/dist/ui/display/index.js +1 -0
- package/dist/ui/forms/LoginForm.d.ts +1 -0
- package/dist/ui/forms/LoginForm.js +1 -0
- package/dist/ui/forms/SettingsContainer.d.ts +2 -1
- package/dist/ui/forms/SettingsContainer.js +2 -2
- package/dist/ui/forms/catalog.d.ts +1 -1
- package/dist/ui/index.d.ts +6 -1
- package/dist/ui/index.js +3 -1
- package/dist/ui/layout/BarLayout.d.ts +1 -1
- package/dist/ui/layout/FloatingLayout.d.ts +1 -1
- package/dist/ui/layout/InlineLayout.d.ts +1 -1
- package/dist/ui/layout/MountTitle.d.ts +2 -2
- package/dist/ui/layout/MountTitle.js +2 -2
- package/dist/ui/layout/RailLayout.d.ts +1 -1
- package/dist/ui/layout/SectionHeader.d.ts +2 -2
- package/dist/ui/layout/SectionHeader.js +4 -7
- package/dist/ui/layout/SingleLayout.d.ts +1 -1
- package/dist/ui/layout/SpatialCard.d.ts +2 -2
- package/dist/ui/layout/SpatialLayout.d.ts +1 -1
- package/dist/ui/layout/SplitLayout.d.ts +1 -1
- package/dist/ui/layout/StackHeader.d.ts +1 -1
- package/dist/ui/layout/StackLayout.d.ts +1 -1
- package/dist/ui/layout/TabLayout.d.ts +1 -1
- package/dist/ui/layout/TabPanelLayout.d.ts +2 -2
- package/dist/ui/layout/TabPanelLayout.js +1 -1
- package/dist/ui/layout/catalog.d.ts +1 -1
- package/dist/ui/layout/register.d.ts +2 -2
- package/dist/ui/layout/register.js +1 -1
- package/dist/ui/media/catalog.d.ts +1 -1
- package/dist/ui/menu/ActionList.d.ts +2 -2
- package/dist/ui/menu/ActionList.js +1 -1
- package/dist/ui/menu/ContainerMenu.d.ts +2 -2
- package/dist/ui/menu/ContextMenu.d.ts +2 -2
- package/dist/ui/menu/useChatPicker.d.ts +2 -2
- package/dist/ui/navigation/ActivityBarItem.css +81 -0
- package/dist/ui/navigation/ActivityBarItem.d.ts +61 -0
- package/dist/ui/navigation/ActivityBarItem.js +68 -0
- package/dist/ui/navigation/catalog.d.ts +1 -1
- package/dist/ui/navigation/catalog.js +1 -0
- package/dist/ui/navigation/index.d.ts +2 -0
- package/dist/ui/navigation/index.js +1 -0
- package/dist/ui/overlay/catalog.d.ts +1 -1
- package/dist/ui/register.d.ts +3 -3
- package/dist/ui/register.js +1 -1
- package/package.json +15 -7
- package/dist/controls/command.d.ts +0 -18
- package/dist/controls/keybinding.d.ts +0 -9
- package/dist/controls/layout.d.ts +0 -16
- package/dist/controls/manifest.d.ts +0 -21
- package/dist/controls/session.d.ts +0 -16
- package/dist/core/binding-resolver.d.ts +0 -7
- package/dist/core/converter-registry.d.ts +0 -2
- package/dist/core/label.d.ts +0 -2
- package/dist/core/mount-surface.d.ts +0 -15
- package/dist/runtime/builtins.d.ts +0 -3
- package/dist/types/disposable.d.ts +0 -3
- package/dist/types/disposable.js +0 -1
- package/dist/types/label.js +0 -1
- /package/dist/{controls → core/controls}/index.d.ts +0 -0
- /package/dist/{controls → core/controls}/index.js +0 -0
- /package/dist/{controls → core/controls}/storage/layout-local.js +0 -0
- /package/dist/{controls → core/controls}/storage/session-local.js +0 -0
- /package/dist/{converters → core/converters}/a2ui-v0.10.js +0 -0
- /package/dist/{converters → core/converters}/index.d.ts +0 -0
- /package/dist/{converters → core/converters}/index.js +0 -0
- /package/dist/core/{surface-presentation.js → graph/surface-presentation.js} +0 -0
- /package/dist/{i18n → core/i18n}/i18n-backend.js +0 -0
- /package/dist/{i18n → core/i18n}/registry.d.ts +0 -0
- /package/dist/{i18n → core/i18n}/registry.js +0 -0
- /package/dist/core/{path-resolver.js → resolve/path-resolver.js} +0 -0
- /package/dist/core/{permissions.js → resolve/permissions.js} +0 -0
- /package/dist/core/{backends → store/backends}/modus-backend.js +0 -0
- /package/dist/core/{backends → store/backends}/storage-backend.js +0 -0
- /package/dist/core/{backends → store/backends}/yjs-backend.js +0 -0
- /package/dist/core/{computed-dsl.d.ts → store/computed-dsl.d.ts} +0 -0
- /package/dist/core/{computed-dsl.js → store/computed-dsl.js} +0 -0
- /package/dist/{runtime → sdk}/args-schema.js +0 -0
- /package/dist/{types → sdk}/binding-resolver.d.ts +0 -0
- /package/dist/{types → sdk}/binding-resolver.js +0 -0
- /package/dist/{types → sdk}/colors.d.ts +0 -0
- /package/dist/{types → sdk}/colors.js +0 -0
- /package/dist/{types → sdk}/command.d.ts +0 -0
- /package/dist/{types → sdk}/command.js +0 -0
- /package/dist/{types → sdk}/component-graph.d.ts +0 -0
- /package/dist/{types → sdk}/component-graph.js +0 -0
- /package/dist/{types → sdk}/component-registry.d.ts +0 -0
- /package/dist/{types → sdk}/component-registry.js +0 -0
- /package/dist/{types → sdk}/converter-registry.d.ts +0 -0
- /package/dist/{types → sdk}/converter-registry.js +0 -0
- /package/dist/{core → sdk}/disposable.js +0 -0
- /package/dist/{types → sdk}/events.d.ts +0 -0
- /package/dist/{types → sdk}/events.js +0 -0
- /package/dist/{types → sdk}/host.d.ts +0 -0
- /package/dist/{types → sdk}/host.js +0 -0
- /package/dist/{types → sdk}/keybinding.d.ts +0 -0
- /package/dist/{types → sdk}/keybinding.js +0 -0
- /package/dist/{types → sdk}/layout.js +0 -0
- /package/dist/{types → sdk}/manifest.d.ts +0 -0
- /package/dist/{types → sdk}/manifest.js +0 -0
- /package/dist/{types → sdk}/mount-menu.d.ts +0 -0
- /package/dist/{types → sdk}/mount-menu.js +0 -0
- /package/dist/{types → sdk}/mount-surface.js +0 -0
- /package/dist/{types → sdk}/permissions.d.ts +0 -0
- /package/dist/{types → sdk}/permissions.js +0 -0
- /package/dist/{types → sdk}/query.d.ts +0 -0
- /package/dist/{types → sdk}/query.js +0 -0
- /package/dist/{types → sdk}/reactive-store.d.ts +0 -0
- /package/dist/{types → sdk}/reactive-store.js +0 -0
- /package/dist/{types → sdk}/scena.js +0 -0
- /package/dist/{types → sdk}/scope-backend.d.ts +0 -0
- /package/dist/{types → sdk}/scope-backend.js +0 -0
- /package/dist/{types → sdk}/session.d.ts +0 -0
- /package/dist/{types → sdk}/session.js +0 -0
- /package/dist/{types → sdk}/shell.d.ts +0 -0
- /package/dist/{types → sdk}/shell.js +0 -0
- /package/dist/{types → sdk}/when.d.ts +0 -0
- /package/dist/{types → sdk}/when.js +0 -0
package/dist/ui/_utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CSSProperties } from 'react';
|
|
2
|
-
import type { Label } from '../
|
|
2
|
+
import type { Label } from '../sdk/label.js';
|
|
3
3
|
export declare function labelText(label?: Label): string;
|
|
4
4
|
export type StyleJustify = 'start' | 'center' | 'end' | 'spaceAround' | 'spaceBetween' | 'spaceEvenly' | 'stretch' | 'space-around' | 'space-between';
|
|
5
5
|
export type StyleAlign = 'start' | 'center' | 'end' | 'stretch';
|
package/dist/ui/_utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolveLabel } from '../
|
|
2
|
-
import { translate } from '../i18n/registry.js';
|
|
1
|
+
import { resolveLabel } from '../sdk/label.js';
|
|
2
|
+
import { translate } from '../core/i18n/registry.js';
|
|
3
3
|
// Resolve a Label (string | { t }) to a plain string for non-reactive contexts.
|
|
4
4
|
// `{ path }` titles are resolved reactively by the layout headers via
|
|
5
5
|
// MountTitle / useMountTitle (which descend + subscribe against the mount's
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useSyncExternalStore } from 'react';
|
|
3
3
|
import { useCampus } from './context.js';
|
|
4
|
-
import { resolveColorAlpha } from '../../
|
|
4
|
+
import { resolveColorAlpha } from '../../sdk/colors.js';
|
|
5
5
|
const DEFAULT_W = 160;
|
|
6
6
|
const DEFAULT_H = 110;
|
|
7
7
|
const DEFAULT_PADDING = 12;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ComponentDefinition } from '../../
|
|
1
|
+
import type { ComponentDefinition } from '../../sdk/component-registry.js';
|
|
2
2
|
export declare const CATALOG_CHART_MAP: ComponentDefinition[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
.oo-button-bar {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--oo-spacing-xs);
|
|
5
|
+
height: var(--oo-button-bar-height, 22px);
|
|
6
|
+
padding: 0 var(--oo-spacing-sm);
|
|
7
|
+
border: none;
|
|
8
|
+
border-radius: var(--oo-radius-sm);
|
|
9
|
+
background: transparent;
|
|
10
|
+
color: inherit;
|
|
11
|
+
font: inherit;
|
|
12
|
+
font-size: var(--oo-font-size-xs);
|
|
13
|
+
line-height: 1;
|
|
14
|
+
white-space: nowrap;
|
|
15
|
+
cursor: default;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.oo-button-bar[data-interactive='true'] { cursor: pointer; }
|
|
19
|
+
.oo-button-bar[data-interactive='true']:hover { background: var(--oo-color-hover); }
|
|
20
|
+
.oo-button-bar[data-interactive='true']:active { background: var(--oo-color-active); }
|
|
21
|
+
|
|
22
|
+
.oo-button-bar:focus-visible {
|
|
23
|
+
outline: 1px solid var(--oo-color-accent);
|
|
24
|
+
outline-offset: -1px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.oo-button-bar[disabled] { opacity: 0.5; cursor: default; }
|
|
28
|
+
.oo-button-bar[data-active='true'] {
|
|
29
|
+
background: var(--oo-color-active);
|
|
30
|
+
color: var(--oo-color-accent);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.oo-button-bar[data-tone='accent'] { color: var(--oo-color-accent); }
|
|
34
|
+
.oo-button-bar[data-tone='danger'] { color: var(--oo-color-danger); }
|
|
35
|
+
.oo-button-bar[data-tone='warning'] { color: var(--oo-color-warning); }
|
|
36
|
+
.oo-button-bar[data-tone='success'] { color: var(--oo-color-success); }
|
|
37
|
+
.oo-button-bar[data-tone='muted'] { color: var(--oo-color-muted); }
|
|
38
|
+
|
|
39
|
+
.oo-button-bar__value { font-weight: 600; }
|
|
40
|
+
|
|
41
|
+
/* In a bar, quieter and smaller -- keyed on the ROLE the shell stamped, not on
|
|
42
|
+
* a surface name, so an app's own `alert:top` gets the same treatment without
|
|
43
|
+
* scena having heard of it.
|
|
44
|
+
*
|
|
45
|
+
* It deliberately does NOT distinguish the title bar from the status bar. That
|
|
46
|
+
* difference is real in most editors, but it is an app's opinion about its own
|
|
47
|
+
* two bars, and an app can express it in one rule keyed on `data-surface`,
|
|
48
|
+
* which it is free to do because it knows its own surface names. scena styling
|
|
49
|
+
* the kind, apps styling the identity, is the line.
|
|
50
|
+
*
|
|
51
|
+
* The attribute plus the class beats `.oo-bar__slot button` in bar.css (one
|
|
52
|
+
* class + one type) rather than tying on specificity and losing to source
|
|
53
|
+
* order. */
|
|
54
|
+
[data-surface-role='bar'] .oo-button-bar {
|
|
55
|
+
height: var(--oo-button-bar-height-sm, 20px);
|
|
56
|
+
color: var(--oo-color-muted);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[data-surface-role='bar'] .oo-button-bar[data-interactive='true']:hover {
|
|
60
|
+
color: var(--oo-color-text);
|
|
61
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import type { BindingPath } from '../../sdk/component-graph.js';
|
|
3
|
+
import './ButtonBar.css';
|
|
4
|
+
export type ButtonBarTone = 'default' | 'accent' | 'danger' | 'warning' | 'success' | 'muted';
|
|
5
|
+
export interface ButtonBarProps {
|
|
6
|
+
icon?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
value?: string | number;
|
|
9
|
+
valuePath?: BindingPath;
|
|
10
|
+
command?: string;
|
|
11
|
+
args?: Record<string, unknown>;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
tone?: ButtonBarTone;
|
|
14
|
+
active?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
title?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
style?: CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The button that goes in a bar.
|
|
22
|
+
*
|
|
23
|
+
* One component for the title bar and the status bar rather than two, because
|
|
24
|
+
* the difference between them is entirely presentational — the status bar is
|
|
25
|
+
* smaller and quieter — and that difference is carried by the surrounding
|
|
26
|
+
* `.oo-bar` context in CSS, not by a prop. A caller that wants the status-bar
|
|
27
|
+
* treatment puts it in the status bar.
|
|
28
|
+
*
|
|
29
|
+
* Both apps grew a near-identical `CommandButton` for this; the useful part is
|
|
30
|
+
* the parts they each got slightly wrong. It renders nothing when there is
|
|
31
|
+
* nothing to show, it does not swallow a click while a command is missing, and
|
|
32
|
+
* `title` falls back to the label so an icon-only button is never unlabelled
|
|
33
|
+
* for a screen reader.
|
|
34
|
+
*/
|
|
35
|
+
export declare function ButtonBar({ icon, label, value, valuePath, command, args, onClick, tone, active, disabled, title, className, style, }: ButtonBarProps): ReactNode;
|
|
36
|
+
export default ButtonBar;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useScena } from '../../react/ScenaProvider.js';
|
|
3
|
+
import { useStore } from '../../react/hooks/useStore.js';
|
|
4
|
+
import './ButtonBar.css';
|
|
5
|
+
/**
|
|
6
|
+
* The button that goes in a bar.
|
|
7
|
+
*
|
|
8
|
+
* One component for the title bar and the status bar rather than two, because
|
|
9
|
+
* the difference between them is entirely presentational — the status bar is
|
|
10
|
+
* smaller and quieter — and that difference is carried by the surrounding
|
|
11
|
+
* `.oo-bar` context in CSS, not by a prop. A caller that wants the status-bar
|
|
12
|
+
* treatment puts it in the status bar.
|
|
13
|
+
*
|
|
14
|
+
* Both apps grew a near-identical `CommandButton` for this; the useful part is
|
|
15
|
+
* the parts they each got slightly wrong. It renders nothing when there is
|
|
16
|
+
* nothing to show, it does not swallow a click while a command is missing, and
|
|
17
|
+
* `title` falls back to the label so an icon-only button is never unlabelled
|
|
18
|
+
* for a screen reader.
|
|
19
|
+
*/
|
|
20
|
+
export function ButtonBar({ icon, label, value, valuePath, command, args, onClick, tone = 'default', active, disabled, title, className, style, }) {
|
|
21
|
+
const scena = useScena();
|
|
22
|
+
const bound = useStore(valuePath);
|
|
23
|
+
const shown = valuePath ? bound : value;
|
|
24
|
+
// Nothing to render is not an error, but an empty button is a dead 24px of
|
|
25
|
+
// bar that still takes a click.
|
|
26
|
+
if (icon === undefined && label === undefined && shown === undefined)
|
|
27
|
+
return null;
|
|
28
|
+
const accessible = title ?? label ?? command;
|
|
29
|
+
function handleClick() {
|
|
30
|
+
if (disabled)
|
|
31
|
+
return;
|
|
32
|
+
if (onClick) {
|
|
33
|
+
onClick();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (command)
|
|
37
|
+
void scena.commands.execute(command, args);
|
|
38
|
+
}
|
|
39
|
+
const interactive = !disabled && (onClick !== undefined || command !== undefined);
|
|
40
|
+
return (_jsxs("button", { type: "button", className: ['oo-button-bar', className].filter(Boolean).join(' '), style: style, "data-tone": tone, "data-active": active ?? undefined, "data-interactive": interactive, disabled: disabled, title: accessible, "aria-label": accessible, "aria-pressed": active, onClick: handleClick, children: [icon !== undefined ? (_jsx("span", { className: "oo-button-bar__icon", "aria-hidden": "true", children: icon })) : null, label !== undefined ? _jsx("span", { className: "oo-button-bar__label", children: label }) : null, shown !== undefined ? _jsx("span", { className: "oo-button-bar__value", children: shown }) : null] }));
|
|
41
|
+
}
|
|
42
|
+
export default ButtonBar;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.oo-theme-mode-toggle {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--oo-spacing-xs);
|
|
5
|
+
height: 22px;
|
|
6
|
+
padding: 0 var(--oo-spacing-sm);
|
|
7
|
+
border: none;
|
|
8
|
+
border-radius: var(--oo-radius-sm);
|
|
9
|
+
background: transparent;
|
|
10
|
+
color: inherit;
|
|
11
|
+
font: inherit;
|
|
12
|
+
font-size: var(--oo-font-size-xs);
|
|
13
|
+
line-height: 1;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.oo-theme-mode-toggle:hover { background: var(--oo-color-hover); }
|
|
18
|
+
.oo-theme-mode-toggle:focus-visible {
|
|
19
|
+
outline: 1px solid var(--oo-color-accent);
|
|
20
|
+
outline-offset: -1px;
|
|
21
|
+
}
|
|
22
|
+
.oo-theme-mode-toggle[data-mode='system'] { color: var(--oo-color-muted); }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { type ThemeModeChoice } from '../../styles/controller.js';
|
|
3
|
+
import './ThemeModeToggle.css';
|
|
4
|
+
export interface ThemeModeToggleProps {
|
|
5
|
+
modes?: ThemeModeChoice[];
|
|
6
|
+
display?: 'icon' | 'text' | 'both';
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cycles the theme mode. A view over `$/ui/theme/mode` — it writes the store
|
|
12
|
+
* and nothing else, so `registerThemeController` stays the only thing that
|
|
13
|
+
* touches the DOM or localStorage, and any other control over the same path
|
|
14
|
+
* (a settings select, say) stays in agreement with it for free.
|
|
15
|
+
*
|
|
16
|
+
* Requires `registerThemeController`; without it the path is never applied and
|
|
17
|
+
* clicking appears to do nothing.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ThemeModeToggle({ modes, display, className, style, }: ThemeModeToggleProps): ReactNode;
|
|
20
|
+
export default ThemeModeToggle;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useStore, useStoreSetter } from '../../react/hooks/useStore.js';
|
|
3
|
+
import { THEME_MODE_PATH } from '../../styles/controller.js';
|
|
4
|
+
import './ThemeModeToggle.css';
|
|
5
|
+
const ORDER = ['light', 'dark', 'system'];
|
|
6
|
+
const ICON = {
|
|
7
|
+
light: '☀︎',
|
|
8
|
+
dark: '☾︎',
|
|
9
|
+
system: '◐︎',
|
|
10
|
+
};
|
|
11
|
+
const TITLE = {
|
|
12
|
+
light: 'Light',
|
|
13
|
+
dark: 'Dark',
|
|
14
|
+
system: 'Follow system',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Cycles the theme mode. A view over `$/ui/theme/mode` — it writes the store
|
|
18
|
+
* and nothing else, so `registerThemeController` stays the only thing that
|
|
19
|
+
* touches the DOM or localStorage, and any other control over the same path
|
|
20
|
+
* (a settings select, say) stays in agreement with it for free.
|
|
21
|
+
*
|
|
22
|
+
* Requires `registerThemeController`; without it the path is never applied and
|
|
23
|
+
* clicking appears to do nothing.
|
|
24
|
+
*/
|
|
25
|
+
export function ThemeModeToggle({ modes = ORDER, display = 'icon', className, style, }) {
|
|
26
|
+
const mode = useStore(THEME_MODE_PATH) ?? 'system';
|
|
27
|
+
const setStore = useStoreSetter();
|
|
28
|
+
const index = modes.indexOf(mode);
|
|
29
|
+
// An unlisted current mode would make indexOf return -1 and the first click
|
|
30
|
+
// land on modes[0] — which is the right recovery, so it is left alone.
|
|
31
|
+
const next = modes[(index + 1) % modes.length] ?? modes[0];
|
|
32
|
+
return (_jsxs("button", { type: "button", className: ['oo-theme-mode-toggle', className].filter(Boolean).join(' '), style: style, "data-mode": mode, title: `${TITLE[mode]} — click for ${TITLE[next].toLowerCase()}`, "aria-label": `Theme mode: ${TITLE[mode]}`, onClick: () => setStore(THEME_MODE_PATH, next), children: [display !== 'text' ? _jsx("span", { "aria-hidden": "true", children: ICON[mode] }) : null, display !== 'icon' ? _jsx("span", { children: TITLE[mode] }) : null] }));
|
|
33
|
+
}
|
|
34
|
+
export default ThemeModeToggle;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.oo-theme-picker {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--oo-spacing-xs);
|
|
5
|
+
font-size: var(--oo-font-size-xs);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.oo-theme-picker__label { color: var(--oo-color-muted); }
|
|
9
|
+
|
|
10
|
+
.oo-theme-picker__select {
|
|
11
|
+
height: 22px;
|
|
12
|
+
padding: 0 var(--oo-spacing-xs);
|
|
13
|
+
border: 1px solid var(--oo-input-border);
|
|
14
|
+
border-radius: var(--oo-radius-sm);
|
|
15
|
+
background: var(--oo-input-bg);
|
|
16
|
+
color: var(--oo-input-fg);
|
|
17
|
+
font: inherit;
|
|
18
|
+
font-size: var(--oo-font-size-xs);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.oo-theme-picker__select:focus-visible {
|
|
22
|
+
outline: 1px solid var(--oo-color-accent);
|
|
23
|
+
outline-offset: -1px;
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import './ThemePicker.css';
|
|
3
|
+
export interface ThemePickerProps {
|
|
4
|
+
themes?: string[];
|
|
5
|
+
label?: string;
|
|
6
|
+
compact?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Picks the theme family. A view over `$/ui/theme/id`, same contract as
|
|
12
|
+
* `ThemeModeToggle`: it writes the store and `registerThemeController` does
|
|
13
|
+
* the applying.
|
|
14
|
+
*
|
|
15
|
+
* The options come from the theme registry rather than from a prop, so a theme
|
|
16
|
+
* registered later — by an extension, or lazily — appears here without this
|
|
17
|
+
* component knowing anything about it. That is also why it reads the registry
|
|
18
|
+
* on render instead of caching: `registerTheme` has no change event, and the
|
|
19
|
+
* list is short enough that it does not matter.
|
|
20
|
+
*
|
|
21
|
+
* Renders nothing below two choices. One theme is not a choice, and a select
|
|
22
|
+
* with a single option is a control that cannot do anything — an app that ships
|
|
23
|
+
* only the built-in theme can mount this unconditionally and it stays out of
|
|
24
|
+
* the way until a second theme is registered.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ThemePicker({ themes, label, compact, className, style, }: ThemePickerProps): ReactNode;
|
|
27
|
+
export default ThemePicker;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useStore, useStoreSetter } from '../../react/hooks/useStore.js';
|
|
3
|
+
import { listThemes } from '../../styles/index.js';
|
|
4
|
+
import { THEME_ID_PATH } from '../../styles/controller.js';
|
|
5
|
+
import './ThemePicker.css';
|
|
6
|
+
/**
|
|
7
|
+
* Picks the theme family. A view over `$/ui/theme/id`, same contract as
|
|
8
|
+
* `ThemeModeToggle`: it writes the store and `registerThemeController` does
|
|
9
|
+
* the applying.
|
|
10
|
+
*
|
|
11
|
+
* The options come from the theme registry rather than from a prop, so a theme
|
|
12
|
+
* registered later — by an extension, or lazily — appears here without this
|
|
13
|
+
* component knowing anything about it. That is also why it reads the registry
|
|
14
|
+
* on render instead of caching: `registerTheme` has no change event, and the
|
|
15
|
+
* list is short enough that it does not matter.
|
|
16
|
+
*
|
|
17
|
+
* Renders nothing below two choices. One theme is not a choice, and a select
|
|
18
|
+
* with a single option is a control that cannot do anything — an app that ships
|
|
19
|
+
* only the built-in theme can mount this unconditionally and it stays out of
|
|
20
|
+
* the way until a second theme is registered.
|
|
21
|
+
*/
|
|
22
|
+
export function ThemePicker({ themes, label = 'Theme', compact = false, className, style, }) {
|
|
23
|
+
const current = useStore(THEME_ID_PATH) ?? 'default';
|
|
24
|
+
const setStore = useStoreSetter();
|
|
25
|
+
const all = listThemes();
|
|
26
|
+
const items = themes
|
|
27
|
+
? themes.map((id) => all.find((t) => t.id === id)).filter((t) => t !== undefined)
|
|
28
|
+
: all;
|
|
29
|
+
if (items.length < 2)
|
|
30
|
+
return null;
|
|
31
|
+
return (_jsxs("label", { className: ['oo-theme-picker', className].filter(Boolean).join(' '), style: style, title: label, children: [compact ? null : _jsx("span", { className: "oo-theme-picker__label", children: label }), _jsx("select", { className: "oo-theme-picker__select", value: current, "aria-label": label, onChange: (e) => setStore(THEME_ID_PATH, e.currentTarget.value), children: items.map((theme) => (_jsx("option", { value: theme.id, children: theme.label }, theme.id))) })] }));
|
|
32
|
+
}
|
|
33
|
+
export default ThemePicker;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ComponentDefinition } from '../../
|
|
1
|
+
import type { ComponentDefinition } from '../../sdk/component-registry.js';
|
|
2
2
|
export declare const CATALOG_CONTROLS_MAP: ComponentDefinition[];
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { Button } from './Button.js';
|
|
1
2
|
// Catalog registration for control components (dynamic import — no eager load).
|
|
2
3
|
export const CATALOG_CONTROLS_MAP = [
|
|
3
|
-
{ component: 'Button', category: 'inline', renderer: { kind: 'react', load:
|
|
4
|
+
{ component: 'Button', category: 'inline', renderer: { kind: 'react', load: async () => ({ default: Button }) } },
|
|
4
5
|
{ component: 'TextField', category: 'inline', renderer: { kind: 'react', load: () => import('./TextField.js').then((m) => ({ default: m.TextField })) } },
|
|
5
6
|
{ component: 'CheckBox', category: 'inline', renderer: { kind: 'react', load: () => import('./CheckBox.js').then((m) => ({ default: m.CheckBox })) } },
|
|
6
7
|
{ component: 'ChoicePicker', category: 'inline', renderer: { kind: 'react', load: () => import('./ChoicePicker.js').then((m) => ({ default: m.ChoicePicker })) } },
|
|
7
8
|
{ component: 'Slider', category: 'inline', renderer: { kind: 'react', load: () => import('./Slider.js').then((m) => ({ default: m.Slider })) } },
|
|
8
9
|
{ component: 'DateTimeInput', category: 'inline', renderer: { kind: 'react', load: () => import('./DateTimeInput.js').then((m) => ({ default: m.DateTimeInput })) } },
|
|
9
10
|
{ component: 'LocaleToggle', category: 'inline', renderer: { kind: 'react', load: () => import('./LocaleToggle.js').then((m) => ({ default: m.LocaleToggle })) } },
|
|
11
|
+
{ component: 'ButtonBar', category: 'inline', renderer: { kind: 'react', load: () => import('./ButtonBar.js').then((m) => ({ default: m.ButtonBar })) } },
|
|
12
|
+
{ component: 'ThemePicker', category: 'inline', renderer: { kind: 'react', load: () => import('./ThemePicker.js').then((m) => ({ default: m.ThemePicker })) } },
|
|
13
|
+
{ component: 'ThemeModeToggle', category: 'inline', renderer: { kind: 'react', load: () => import('./ThemeModeToggle.js').then((m) => ({ default: m.ThemeModeToggle })) } },
|
|
10
14
|
];
|
|
@@ -11,3 +11,9 @@ export { Checks } from './Checks.js';
|
|
|
11
11
|
export type { CheckRuleResolved, ChecksProps } from './Checks.js';
|
|
12
12
|
export { LocaleToggle } from './LocaleToggle.js';
|
|
13
13
|
export type { LocaleToggleProps, LocaleToggleDisplay } from './LocaleToggle.js';
|
|
14
|
+
export { ButtonBar } from './ButtonBar.js';
|
|
15
|
+
export type { ButtonBarProps, ButtonBarTone } from './ButtonBar.js';
|
|
16
|
+
export { ThemePicker } from './ThemePicker.js';
|
|
17
|
+
export type { ThemePickerProps } from './ThemePicker.js';
|
|
18
|
+
export { ThemeModeToggle } from './ThemeModeToggle.js';
|
|
19
|
+
export type { ThemeModeToggleProps } from './ThemeModeToggle.js';
|
package/dist/ui/control/index.js
CHANGED
|
@@ -9,3 +9,6 @@ export { Slider } from './Slider.js';
|
|
|
9
9
|
export { DateTimeInput } from './DateTimeInput.js';
|
|
10
10
|
export { Checks } from './Checks.js';
|
|
11
11
|
export { LocaleToggle } from './LocaleToggle.js';
|
|
12
|
+
export { ButtonBar } from './ButtonBar.js';
|
|
13
|
+
export { ThemePicker } from './ThemePicker.js';
|
|
14
|
+
export { ThemeModeToggle } from './ThemeModeToggle.js';
|
package/dist/ui/data/Filter.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CSSProperties, type DragEvent as ReactDragEvent, type ReactNode } from 'react';
|
|
2
|
-
import type { BindingPath } from '../../
|
|
2
|
+
import type { BindingPath } from '../../sdk/component-graph.js';
|
|
3
3
|
import './Listable.css';
|
|
4
4
|
export interface ListableRenderContext {
|
|
5
5
|
tableMode: boolean;
|
|
@@ -48,6 +48,7 @@ export interface ListableProps<T> {
|
|
|
48
48
|
onSortChange?: (next: ListableSort | null) => void;
|
|
49
49
|
tableBreakpoint?: number;
|
|
50
50
|
renderListRow?: (item: T) => ReactNode;
|
|
51
|
+
getRowStyle?: (item: T, renderCtx: ListableRenderContext) => CSSProperties | undefined;
|
|
51
52
|
contextMenuSlot?: string;
|
|
52
53
|
contextFor?: (item: T) => Record<string, unknown>;
|
|
53
54
|
contextDataContext?: BindingPath;
|
|
@@ -57,4 +58,4 @@ export interface ListableProps<T> {
|
|
|
57
58
|
style?: CSSProperties;
|
|
58
59
|
manageKeys?: boolean;
|
|
59
60
|
}
|
|
60
|
-
export declare function Listable<T>({ items, columns, getKey, title, toolbar, selectedKey, onSelect, onActivate, initialSort, sort, onSortChange, tableBreakpoint, renderListRow, contextMenuSlot, contextFor, contextDataContext, onRowDragStart, emptyState, className, style, manageKeys, }: ListableProps<T>): ReactNode;
|
|
61
|
+
export declare function Listable<T>({ items, columns, getKey, title, toolbar, selectedKey, onSelect, onActivate, initialSort, sort, onSortChange, tableBreakpoint, renderListRow, getRowStyle, contextMenuSlot, contextFor, contextDataContext, onRowDragStart, emptyState, className, style, manageKeys, }: ListableProps<T>): ReactNode;
|
package/dist/ui/data/Listable.js
CHANGED
|
@@ -33,7 +33,7 @@ function sortItems(items, columns, sort) {
|
|
|
33
33
|
return [...items].sort((a, b) => sign * cmp(a, b));
|
|
34
34
|
}
|
|
35
35
|
const DEFAULT_TABLE_BREAKPOINT = 480;
|
|
36
|
-
export function Listable({ items, columns, getKey, title, toolbar, selectedKey, onSelect, onActivate, initialSort, sort, onSortChange, tableBreakpoint = DEFAULT_TABLE_BREAKPOINT, renderListRow, contextMenuSlot, contextFor, contextDataContext, onRowDragStart, emptyState, className, style, manageKeys = false, }) {
|
|
36
|
+
export function Listable({ items, columns, getKey, title, toolbar, selectedKey, onSelect, onActivate, initialSort, sort, onSortChange, tableBreakpoint = DEFAULT_TABLE_BREAKPOINT, renderListRow, getRowStyle, contextMenuSlot, contextFor, contextDataContext, onRowDragStart, emptyState, className, style, manageKeys = false, }) {
|
|
37
37
|
const rootRef = useRef(null);
|
|
38
38
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
39
39
|
const [internalSort, setInternalSort] = useState(initialSort ?? null);
|
|
@@ -175,8 +175,10 @@ export function Listable({ items, columns, getKey, title, toolbar, selectedKey,
|
|
|
175
175
|
function renderStackedRow(item) {
|
|
176
176
|
const key = getKey(item);
|
|
177
177
|
const selected = key === selectedKey;
|
|
178
|
+
const styleRow = getRowStyle ? getRowStyle(item, renderCtx) : undefined;
|
|
178
179
|
return (_jsx("div", { role: "row", "data-key": key, "data-selected": selected ? 'true' : undefined, className: "oo-listable__item oo-listable__stack", draggable: onRowDragStart ? true : undefined, onDragStart: onRowDragStart ? (e) => onRowDragStart(e, item) : undefined, onClick: () => onSelect?.(item), onDoubleClick: () => onActivate?.(item), onContextMenu: (e) => openContextMenu(e, item), style: {
|
|
179
|
-
cursor: onSelect ? 'pointer' : undefined
|
|
180
|
+
cursor: onSelect ? 'pointer' : undefined,
|
|
181
|
+
...styleRow,
|
|
180
182
|
}, children: renderListRow
|
|
181
183
|
? renderListRow(item)
|
|
182
184
|
: visibleColumns.map((col) => {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
export interface PaginationProps {
|
|
3
3
|
namespace: string;
|
|
4
|
+
count?: 'items' | 'pages';
|
|
5
|
+
pageSizes?: number[];
|
|
6
|
+
always?: boolean;
|
|
4
7
|
}
|
|
5
|
-
export declare function Pagination({ namespace }: PaginationProps): ReactNode;
|
|
8
|
+
export declare function Pagination({ namespace, count, pageSizes, always, }: PaginationProps): ReactNode;
|
|
@@ -4,23 +4,40 @@ import { useStore } from '../../react/hooks/useStore.js';
|
|
|
4
4
|
import { useI18n } from '../../react/hooks/useI18n.js';
|
|
5
5
|
// Generic pager bound to a resource's query/view. Writes `$/<ns>/query.page`;
|
|
6
6
|
// the data provider re-resolves the view (server window or client slice).
|
|
7
|
-
|
|
8
|
-
export function Pagination({ namespace }) {
|
|
7
|
+
export function Pagination({ namespace, count = 'items', pageSizes, always = false, }) {
|
|
9
8
|
const scena = useScena();
|
|
10
9
|
const { t } = useI18n('query');
|
|
11
10
|
const view = useStore(`$/${namespace}/view`);
|
|
12
11
|
const query = useStore(`$/${namespace}/query`);
|
|
13
12
|
const total = view?.total ?? 0;
|
|
14
|
-
const page = query?.page ?? 1;
|
|
15
13
|
const pageSize = query?.pageSize ?? 50;
|
|
16
14
|
const pages = Math.max(1, Math.ceil(total / pageSize));
|
|
17
|
-
|
|
15
|
+
// Clamped, because `total` can shrink under a reader - a filter narrowing,
|
|
16
|
+
// rows being deleted - and page 7 of a 2-page list renders as empty with
|
|
17
|
+
// both arrows disabled, which looks like a broken list rather than a stale
|
|
18
|
+
// page number.
|
|
19
|
+
const page = Math.min(Math.max(1, query?.page ?? 1), pages);
|
|
20
|
+
if (total <= pageSize && !always && !pageSizes?.length)
|
|
18
21
|
return null;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// The window this page actually covers. `first` is 0 only when there is
|
|
23
|
+
// nothing at all, which is the one case where "1 – 0 of 0" would be absurd.
|
|
24
|
+
const first = total === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
25
|
+
const last = Math.min(page * pageSize, total);
|
|
26
|
+
const write = (next) => scena.store.set(`$/${namespace}/query`, { ...query, ...next });
|
|
27
|
+
const setPage = (p) => write({ page: Math.min(pages, Math.max(1, p)) });
|
|
28
|
+
const label = count === 'pages'
|
|
29
|
+
? t('pageOf', { page, pages, fallback: '{page} / {pages}' })
|
|
30
|
+
: total === 0
|
|
31
|
+
? t('noItems', { fallback: 'nothing' })
|
|
32
|
+
: t('itemRange', {
|
|
33
|
+
first,
|
|
34
|
+
last,
|
|
35
|
+
total,
|
|
36
|
+
fallback: '{first} – {last} of {total}',
|
|
37
|
+
});
|
|
38
|
+
return (_jsxs("div", { style: bar, children: [_jsx("button", { style: btn, disabled: page <= 1, onClick: () => setPage(page - 1), "aria-label": "Previous", children: "\u2039" }), _jsx("span", { style: { fontSize: 12, color: 'var(--oo-color-muted, #8a8f98)', fontVariantNumeric: 'tabular-nums' }, "aria-live": "polite", children: label }), _jsx("button", { style: btn, disabled: page >= pages, onClick: () => setPage(page + 1), "aria-label": "Next", children: "\u203A" }), pageSizes && pageSizes.length > 0 ? (_jsx("select", { style: select, value: pageSize, "aria-label": t('perPage', { fallback: 'Rows per page' }), onChange: (event) => write({ page: 1, pageSize: Number(event.currentTarget.value) }), children: [...new Set([...pageSizes, pageSize])]
|
|
39
|
+
.sort((a, b) => a - b)
|
|
40
|
+
.map((size) => (_jsx("option", { value: size, children: t('perPageOption', { size, fallback: '{size} / page' }) }, size))) })) : null] }));
|
|
24
41
|
}
|
|
25
42
|
const bar = {
|
|
26
43
|
display: 'flex',
|
|
@@ -41,3 +58,14 @@ const btn = {
|
|
|
41
58
|
height: 24,
|
|
42
59
|
lineHeight: '20px',
|
|
43
60
|
};
|
|
61
|
+
const select = {
|
|
62
|
+
appearance: 'none',
|
|
63
|
+
background: 'var(--oo-color-canvas, #14171c)',
|
|
64
|
+
border: '1px solid var(--oo-color-border, #2b2f37)',
|
|
65
|
+
borderRadius: 4,
|
|
66
|
+
color: 'var(--oo-color-muted, #8a8f98)',
|
|
67
|
+
cursor: 'pointer',
|
|
68
|
+
font: 'inherit',
|
|
69
|
+
fontSize: 11,
|
|
70
|
+
padding: '2px 4px',
|
|
71
|
+
};
|
package/dist/ui/data/Tree.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ClipboardEvent as ReactClipboardEvent, type CSSProperties, type DragEvent as ReactDragEvent, type ReactNode } from 'react';
|
|
2
|
-
import type { BindingPath } from '../../
|
|
3
|
-
import type { PickerAction } from '../../
|
|
2
|
+
import type { BindingPath } from '../../sdk/component-graph.js';
|
|
3
|
+
import type { PickerAction } from '../../sdk/host.js';
|
|
4
4
|
import './Tree.css';
|
|
5
5
|
export interface TreeNode<T = unknown> {
|
|
6
6
|
key: string;
|
package/dist/ui/data/Tree.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { ContextMenu } from '../menu/ContextMenu.js';
|
|
4
4
|
import './Tree.css';
|
|
5
|
-
import { resolveColorVar } from '../../
|
|
5
|
+
import { resolveColorVar } from '../../sdk/colors.js';
|
|
6
6
|
function flatten(nodes, expanded, depth = 0, parentKey = null, out = []) {
|
|
7
7
|
for (const n of nodes) {
|
|
8
8
|
const hasChildren = n.children !== undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ComponentDefinition } from '../../
|
|
1
|
+
import type { ComponentDefinition } from '../../sdk/component-registry.js';
|
|
2
2
|
export declare const CATALOG_DATA_MAP: ComponentDefinition[];
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
.oo-alert {
|
|
2
|
+
--oo-alert-rgb: var(--oo-rgb-blue);
|
|
3
|
+
--oo-alert-color: rgb(var(--oo-alert-rgb));
|
|
4
|
+
--oo-alert-spacing: var(--oo-spacing-sm);
|
|
5
|
+
--oo-alert-background: rgb(var(--oo-alert-rgb) / 0.1);
|
|
6
|
+
--oo-alert-border: rgb(var(--oo-alert-rgb) / 0.25);
|
|
2
7
|
display: flex;
|
|
3
8
|
align-items: flex-start;
|
|
4
|
-
gap: var(--oo-spacing
|
|
5
|
-
padding: var(--oo-spacing
|
|
9
|
+
gap: var(--oo-alert-spacing);
|
|
10
|
+
padding: var(--oo-alert-spacing);
|
|
6
11
|
border-radius: var(--oo-radius-md, 8px);
|
|
7
12
|
font-size: var(--oo-font-size-sm);
|
|
8
13
|
line-height: 1.5;
|
|
9
|
-
background:
|
|
10
|
-
color:
|
|
11
|
-
border: 1px solid
|
|
14
|
+
background: var(--oo-alert-background);
|
|
15
|
+
color: var(--oo-alert-color);
|
|
16
|
+
border: 1px solid var(--oo-alert-border);
|
|
12
17
|
}
|
|
13
|
-
.oo-alert[data-tone='info'] { --oo-
|
|
14
|
-
.oo-alert[data-tone='success'] { --oo-
|
|
15
|
-
.oo-alert[data-tone='warning'] { --oo-
|
|
16
|
-
.oo-alert[data-tone='danger'] { --oo-
|
|
18
|
+
.oo-alert[data-tone='info'] { --oo-alert-rgb: var(--oo-rgb-blue); }
|
|
19
|
+
.oo-alert[data-tone='success'] { --oo-alert-rgb: var(--oo-rgb-green); }
|
|
20
|
+
.oo-alert[data-tone='warning'] { --oo-alert-rgb: var(--oo-rgb-amber); }
|
|
21
|
+
.oo-alert[data-tone='danger'] { --oo-alert-rgb: var(--oo-rgb-red); }
|
|
17
22
|
.oo-alert__icon {
|
|
18
23
|
flex: 0 0 auto;
|
|
19
24
|
margin-top: 2px;
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
color: var(--oo-color-fg);
|
|
28
33
|
}
|
|
29
34
|
.oo-alert__title {
|
|
30
|
-
color: rgb(var(--oo-
|
|
35
|
+
color: rgb(var(--oo-alert-rgb));
|
|
31
36
|
}
|
|
32
37
|
.oo-alert__close {
|
|
33
38
|
background: none;
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
.oo-badge {
|
|
2
|
+
--oo-badge-gap: 4px;
|
|
2
3
|
--oo-badge-padding-y: 2px;
|
|
3
4
|
--oo-badge-padding-x: 8px;
|
|
5
|
+
--oo-badge-font-size: var(--oo-font-size-xs);
|
|
6
|
+
--oo-badge-font-family: var(--oo-mono);
|
|
7
|
+
--oo-badge-font-weight: 500;
|
|
8
|
+
--oo-badge-line-height: 1.4;
|
|
9
|
+
--oo-badge-rgb: var(--oo-badge-color, var(--oo-rgb-muted));
|
|
10
|
+
--oo-badge-background: rgb(var(--oo-badge-rgb, var(--oo-rgb-muted)) / 0.15);
|
|
11
|
+
--oo-badge-color: rgb(var(--oo-badge-rgb, var(--oo-rgb-muted)));
|
|
12
|
+
|
|
4
13
|
display: inline-flex;
|
|
5
14
|
align-items: center;
|
|
6
|
-
gap:
|
|
15
|
+
gap: var(--oo-badge-gap);
|
|
7
16
|
padding: var(--oo-badge-padding-y) var(--oo-badge-padding-x);
|
|
17
|
+
font-size: var(--oo-badge-font-size);
|
|
18
|
+
font-family: var(--oo-badge-font-family);
|
|
19
|
+
font-weight: var(--oo-badge-font-weight);
|
|
20
|
+
line-height: var(--oo-badge-line-height);
|
|
21
|
+
background: var(--oo-badge-background);
|
|
22
|
+
color: var(--oo-badge-color);
|
|
8
23
|
border-radius: 9999px;
|
|
9
|
-
font-size: var(--oo-font-size-xs);
|
|
10
|
-
font-weight: 500;
|
|
11
|
-
line-height: 1.4;
|
|
12
|
-
background: rgb(var(--oo-color, var(--oo-rgb-muted)) / 0.15);
|
|
13
|
-
color: rgb(var(--oo-color, var(--oo-rgb-muted)));
|
|
14
24
|
white-space: nowrap;
|
|
15
25
|
}
|
|
16
|
-
.oo-badge[data-variant='default'] {
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
.oo-badge[data-variant='
|
|
20
|
-
.oo-badge[data-variant='
|
|
21
|
-
.oo-badge[data-variant='danger'] { --oo-color: var(--oo-rgb-red); }
|
|
22
|
-
.oo-badge[data-variant='info'] { --oo-color: var(--oo-rgb-blue); }
|
|
26
|
+
.oo-badge[data-variant='default'] { --oo-badge-rgb: var(--oo-rgb-muted); }
|
|
27
|
+
.oo-badge[data-variant='success'] { --oo-badge-rgb: var(--oo-rgb-green); }
|
|
28
|
+
.oo-badge[data-variant='warning'] { --oo-badge-rgb: var(--oo-rgb-amber); }
|
|
29
|
+
.oo-badge[data-variant='danger'] { --oo-badge-rgb: var(--oo-rgb-red); }
|
|
30
|
+
.oo-badge[data-variant='info'] { --oo-badge-rgb: var(--oo-rgb-blue); }
|
|
23
31
|
.oo-badge[data-outline='true'] {
|
|
24
32
|
background: transparent;
|
|
25
|
-
border: 1px solid rgb(var(--oo-color, var(--oo-rgb-muted)) / 0.5);
|
|
33
|
+
border: 1px solid rgb(var(--oo-badge-color, var(--oo-rgb-muted)) / 0.5);
|
|
26
34
|
}
|