@owlmeans/web-panel 0.1.18-rc.80 → 0.1.18-rc.81
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 +2 -2
- package/agent-meta/manifest.json +16 -2
- package/agent-meta/skills/web-panel/SKILL.md +4 -2
- package/agent-meta/skills/web-panel/parts/editor.md +47 -0
- package/agent-meta/skills/web-panel/parts/navigation.md +13 -1
- package/build/components/nav/layout.d.ts.map +1 -1
- package/build/components/nav/layout.js +16 -18
- package/build/components/nav/layout.js.map +1 -1
- package/build/components/nav/mobile.d.ts.map +1 -1
- package/build/components/nav/mobile.js +6 -6
- package/build/components/nav/mobile.js.map +1 -1
- package/build/components/nav/side.d.ts.map +1 -1
- package/build/components/nav/side.js +5 -5
- package/build/components/nav/side.js.map +1 -1
- package/build/components/nav/top.d.ts.map +1 -1
- package/build/components/nav/top.js +8 -5
- package/build/components/nav/top.js.map +1 -1
- package/build/components/nav/types.d.ts +3 -0
- package/build/components/nav/types.d.ts.map +1 -1
- package/build/components/nav/viewport.d.ts +3 -0
- package/build/components/nav/viewport.d.ts.map +1 -0
- package/build/components/nav/viewport.js +14 -0
- package/build/components/nav/viewport.js.map +1 -0
- package/build/editor/component.d.ts +4 -0
- package/build/editor/component.d.ts.map +1 -0
- package/build/editor/component.js +103 -0
- package/build/editor/component.js.map +1 -0
- package/build/editor/consts.local.d.ts +6 -0
- package/build/editor/consts.local.d.ts.map +1 -0
- package/build/editor/consts.local.js +58 -0
- package/build/editor/consts.local.js.map +1 -0
- package/build/editor/index.d.ts +3 -0
- package/build/editor/index.d.ts.map +1 -0
- package/build/editor/index.js +2 -0
- package/build/editor/index.js.map +1 -0
- package/build/editor/types.d.ts +24 -0
- package/build/editor/types.d.ts.map +1 -0
- package/build/editor/types.js +2 -0
- package/build/editor/types.js.map +1 -0
- package/build/entrypoints.d.ts +1 -7
- package/build/entrypoints.d.ts.map +1 -1
- package/package.json +35 -21
- package/src/components/nav/layout.tsx +149 -106
- package/src/components/nav/mobile.tsx +113 -76
- package/src/components/nav/side.tsx +39 -23
- package/src/components/nav/top.tsx +40 -19
- package/src/components/nav/types.ts +5 -2
- package/src/components/nav/viewport.ts +16 -0
- package/src/editor/component.tsx +172 -0
- package/src/editor/consts.local.ts +62 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/types.ts +24 -0
- package/tests/editor.spec.ts +58 -0
- package/tests/harness/editor.tsx +32 -0
- package/tests/harness/mount.tsx +10 -4
- package/tests/nav.spec.ts +23 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ but never started.
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
bun add @owlmeans/web-panel@^0.1.18-rc.
|
|
14
|
+
bun add @owlmeans/web-panel@^0.1.18-rc.81
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Peer requirements (the consuming app provides these): `react`, `react-dom`,
|
|
@@ -580,7 +580,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
580
580
|
your project's skill store (`.agents/skills/`):
|
|
581
581
|
|
|
582
582
|
```sh
|
|
583
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
583
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.58
|
|
584
584
|
```
|
|
585
585
|
|
|
586
586
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 3,
|
|
3
3
|
"package": "@owlmeans/web-panel",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-10-
|
|
4
|
+
"version": "0.1.18-rc.81",
|
|
5
|
+
"generatedAt": "2026-10-26T16:31:24.937Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -30,6 +30,20 @@
|
|
|
30
30
|
"@owlmeans/web-panel/consent"
|
|
31
31
|
]
|
|
32
32
|
},
|
|
33
|
+
{
|
|
34
|
+
"name": "editor",
|
|
35
|
+
"file": "skills/web-panel/parts/editor.md",
|
|
36
|
+
"description": "Controlled JSON editing and previews with the optional web-panel/editor entrypoint, CodeMirror 6, syntax diagnostics, formatting, copying and read-only state.",
|
|
37
|
+
"purposes": [
|
|
38
|
+
"form",
|
|
39
|
+
"view"
|
|
40
|
+
],
|
|
41
|
+
"mentions": [
|
|
42
|
+
"JsonEditor",
|
|
43
|
+
"JsonEditorProps",
|
|
44
|
+
"JsonEditorLabels"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
33
47
|
{
|
|
34
48
|
"name": "forms",
|
|
35
49
|
"file": "skills/web-panel/parts/forms.md",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: web-panel
|
|
3
|
-
description: How to use @owlmeans/web-panel — base browser context factory (makeContext) with shadcn/Radix + Tailwind and the default OwlMeans router wired in, the two-layer navigation shell (NavLayout/TopNav/SideNav/Footer), form/panel components, and the standard icon + colour picker (`./picker`: IconColorPicker, ColorPicker, IconPicker, IconTile, ColorDot). Auto-invoked when building a web app's makeContext, wiring its navigation or layout, importing web panel components, or letting a person choose (or showing) an icon or a colour.
|
|
3
|
+
description: How to use @owlmeans/web-panel — base browser context factory (makeContext) with shadcn/Radix + Tailwind and the default OwlMeans router wired in, the two-layer navigation shell (NavLayout/TopNav/SideNav/Footer), form/panel components, and the controlled JSON editor (`./editor`: JsonEditor), the standard icon + colour picker (`./picker`: IconColorPicker, ColorPicker, IconPicker, IconTile, ColorDot). Auto-invoked when building a web app's makeContext, wiring its navigation or layout, importing web panel components, or letting a person choose (or showing) an icon or a colour.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/web-panel
|
|
9
9
|
|
|
10
10
|
**Layer:** Web (React)
|
|
11
|
-
**Install:** `"@owlmeans/web-panel": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/web-panel": "^0.1.18-rc.81"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Choosing a component
|
|
14
14
|
|
|
@@ -36,6 +36,7 @@ root unless the import column names a subpath.
|
|
|
36
36
|
| The same, inline in a dialog the app already owns | `ColorSwatches`, `IconGrid` | `./picker` | `<IconGrid value={icon} onChange={setIcon} color={color} />` |
|
|
37
37
|
| Show a chosen mark or colour (list, chip, header) | `IconTile`, `ColorDot` | `./picker` | `<IconTile icon={type.icon} color={type.tone} size="sm" />` |
|
|
38
38
|
| Palette, icon names, colour maths — no React | `PICKER_*`, `pickerColorHelper`, `pickerIconHelper` | `./picker/vocabulary` (`pickerIconHelper`, `PICKER_ICONS`: `./picker`) | `pickerColorHelper.styleOf(color)` |
|
|
39
|
+
| Editable or read-only JSON | `JsonEditor` | `./editor` | `<JsonEditor key={record.id} value={draft} onChange={setDraft} onValidityChange={setValid} ariaLabel="Record JSON" />` |
|
|
39
40
|
| The current breakpoint | `useBreakPoint` / `useMapBreakpoint` | root | `useMapBreakpoint({ xs: 1, md: 2 }, 3)` |
|
|
40
41
|
|
|
41
42
|
**Common mistakes:**
|
|
@@ -179,6 +180,7 @@ Load a part on top of this core when its job is in play.
|
|
|
179
180
|
|
|
180
181
|
| Part | Load it when |
|
|
181
182
|
|---|---|
|
|
183
|
+
| [editor](parts/editor.md) | Editing or displaying JSON, syntax diagnostics, controlled text, read-only previews and document identity |
|
|
182
184
|
| [navigation](parts/navigation.md) | Building or restyling the application shell — `NavLayout`, `TopNav`/`SideNav`/`MobileNav`/`Footer`, the nav config, `mobileMenu`, the skip link, the footer credit, the header surface |
|
|
183
185
|
| [forms](parts/forms.md) | Writing a form (`Form`, `TextInput`, the buttons) or raising a toast (`Toaster`, `sonner`, the permission-denied toast) |
|
|
184
186
|
| [theme](parts/theme.md) | Adding the light/dark switch (`ThemeToggle`, `useColorScheme`), the head bootstrap from `./scheme`, or CSS that follows the scheme |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: editor
|
|
3
|
+
description: Controlled JSON editing and previews with the optional web-panel/editor entrypoint, CodeMirror 6, syntax diagnostics, formatting, copying and read-only state.
|
|
4
|
+
metadata:
|
|
5
|
+
purposes: form, view
|
|
6
|
+
mentions: JsonEditor, JsonEditorProps, JsonEditorLabels
|
|
7
|
+
---
|
|
8
|
+
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
9
|
+
|
|
10
|
+
# JSON editor
|
|
11
|
+
|
|
12
|
+
Import `JsonEditor` and its `JsonEditorProps` / `JsonEditorLabels` from
|
|
13
|
+
`@owlmeans/web-panel/editor`. The root entrypoint does not import the editor, keeping CodeMirror
|
|
14
|
+
out of consumers that do not use it. JSON support, linting and highlighting are package dependencies.
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
<JsonEditor
|
|
18
|
+
key={record.id}
|
|
19
|
+
value={draft}
|
|
20
|
+
onChange={setDraft}
|
|
21
|
+
onValidityChange={setValid}
|
|
22
|
+
ariaLabel="Record JSON"
|
|
23
|
+
testId="record-json"
|
|
24
|
+
/>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `value` is always raw text. Preserve incomplete drafts; parse at the host's save boundary.
|
|
28
|
+
`onValidityChange` reports syntax validity so a host can disable its save action.
|
|
29
|
+
- A change of document identity keys/remounts the component; an update within the same document
|
|
30
|
+
preserves undo history. External controlled values update the view without echoing `onChange`.
|
|
31
|
+
- `readOnly` disables editing and removes Format; Copy remains available. Use it for previews and
|
|
32
|
+
inspectors. Formatting pretty-prints valid JSON without changing its values.
|
|
33
|
+
- `id`, `testId` and `ariaLabel` address the editable content, including `aria-readonly`,
|
|
34
|
+
`aria-invalid` and its diagnostic description. The wrapper exposes `data-json-editor` and
|
|
35
|
+
`data-valid`. Pass `minHeight` / `maxHeight` in pixels when the host needs another viewport size.
|
|
36
|
+
- `labels` overrides format/copy/copied/copyFailed/readOnly/valid/invalid copy. Components do not
|
|
37
|
+
read an implicit i18n provider; defaults are English.
|
|
38
|
+
- Colours use host shadcn variables, falling back to Tailwind's `--color-*` variables. Brand-specific
|
|
39
|
+
primary/chart colours have standard-token fallbacks. The host still scans web-panel's `src`.
|
|
40
|
+
|
|
41
|
+
Acceptance: `@owlmeans/web-panel`'s `editor.spec.ts` (`bun test tests/editor.spec.ts` in the package); the real Chromium harness checks
|
|
42
|
+
highlighting, formatting, undo, incomplete drafts, validity, external values and read-only controls.
|
|
43
|
+
|
|
44
|
+
## External docs
|
|
45
|
+
|
|
46
|
+
- https://github.com/codemirror/lang-json — `json()` language support and `jsonParseLinter()` diagnostics for CodeMirror 6.
|
|
47
|
+
- https://codemirror.net/docs/ref/ — editor state/view extensions, controlled transactions and read-only configuration.
|
|
@@ -49,7 +49,7 @@ header. Both render the same items; only one is visible at a time.
|
|
|
49
49
|
|
|
50
50
|
| Component | Props |
|
|
51
51
|
|---|---|
|
|
52
|
-
| `NavLayout` | `nav: PanelNavConfig`, `translate?`, `title?: ReactNode`, `home?: string` (brand target — defaults to the first section's first item), `actions?: ReactNode`, `mobileMenu?: boolean`, `skipLinkLabel?: string \| false`, `themeToggle?: boolean \| { labels? }`, `footer?: PanelNavLink[] \| ReactNode`, `headerClassName?`, `contentClassName?`, `containerClassName?`, `className?`, `style?` |
|
|
52
|
+
| `NavLayout` | `nav: PanelNavConfig`, `navTestId?: string`, `translate?`, `title?: ReactNode`, `home?: string` (brand target — defaults to the first section's first item), `actions?: ReactNode`, `mobileMenu?: boolean`, `skipLinkLabel?: string \| false`, `themeToggle?: boolean \| { labels? }`, `footer?: PanelNavLink[] \| ReactNode`, `headerClassName?`, `contentClassName?`, `containerClassName?`, `className?`, `style?` |
|
|
53
53
|
| `TopNav` | `config: PanelNavConfig`, `translate?`, `ariaLabel?`, `className?`, `style?` |
|
|
54
54
|
| `SideNav` | the same, plus `variant?: 'side' \| 'bar'` |
|
|
55
55
|
| `MobileNav` | the same as `TopNav`; `ariaLabel` names the landmark inside the sheet, `className`/`style` land on the trigger button |
|
|
@@ -189,3 +189,15 @@ Rules that make the shell behave:
|
|
|
189
189
|
- **Vendor `navigation-menu` and `sheet`.** `SideNav` builds on the package-local `Button`; `TopNav`
|
|
190
190
|
uses the package-local shadcn `navigation-menu` primitive and `MobileNav` the package-local
|
|
191
191
|
`sheet` (over `@radix-ui/react-dialog`, a peer) — see *Consumer setup — package boundary and Tailwind* in [SKILL.md](../SKILL.md).
|
|
192
|
+
|
|
193
|
+
## Stable selectors and detail screens
|
|
194
|
+
|
|
195
|
+
`PanelNavSection.testId` and `PanelNavItem.testId` optionally name their rendered destinations;
|
|
196
|
+
`NavLayout.navTestId` names the desktop navigation landmark. With `mobileMenu`, only the current
|
|
197
|
+
viewport's section menu is mounted, keeping the same destination selector unique when the sheet
|
|
198
|
+
opens. Side/bar item selectors are emitted only by the visible variant. Hosts keep their selectors
|
|
199
|
+
in navigation data rather than querying translated text or styling classes.
|
|
200
|
+
|
|
201
|
+
When an unlisted detail screen has no listed ancestor, the headless model selects the section of
|
|
202
|
+
the longest listed pathname prefix. This covers sibling route declarations such as `/items` and
|
|
203
|
+
`/items/:id` without changing their route hierarchy.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/components/nav/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,EAAmB,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/components/nav/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,EAAmB,MAAM,OAAO,CAAA;AAUxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAgLxC,CAAA"}
|
|
@@ -4,6 +4,7 @@ import { useNavigate } from '@owlmeans/client';
|
|
|
4
4
|
import { navLabelHelper } from '@owlmeans/client-panel';
|
|
5
5
|
import { cn } from '../../@/lib/utils.js';
|
|
6
6
|
import { Footer } from '../footer/component.js';
|
|
7
|
+
import { useWideNav } from './viewport.js';
|
|
7
8
|
import { MobileNav } from './mobile.js';
|
|
8
9
|
import { SideNav } from './side.js';
|
|
9
10
|
import { TopNav } from './top.js';
|
|
@@ -23,23 +24,24 @@ import { CONTAINER, SKIP_LINK } from './consts.local.js';
|
|
|
23
24
|
* The skip link comes FIRST in the root, ahead of the header — the first thing a keyboard reaches
|
|
24
25
|
* — and moves focus to `<main id="main">`, past the brand, the menus and the actions.
|
|
25
26
|
*/
|
|
26
|
-
export const NavLayout = ({ nav, translate = navLabelHelper.defaultNavTranslate, title, home, actions, footer, children, className, style, headerClassName, contentClassName, containerClassName, mobileMenu = false, skipLinkLabel, themeToggle }) => {
|
|
27
|
+
export const NavLayout = ({ nav, translate = navLabelHelper.defaultNavTranslate, title, home, actions, footer, children, className, style, headerClassName, contentClassName, containerClassName, mobileMenu = false, skipLinkLabel, themeToggle, navTestId }) => {
|
|
27
28
|
const navigator = useNavigate();
|
|
29
|
+
const wide = useWideNav();
|
|
28
30
|
// The switcher's names go through `translate` like every other shell string, with the caller's
|
|
29
31
|
// own labels winning; `false`/absent stays absent, so the footer renders exactly as it did.
|
|
30
|
-
const toggle = themeToggle == null || themeToggle === false
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
const toggle = themeToggle == null || themeToggle === false
|
|
33
|
+
? undefined
|
|
34
|
+
: {
|
|
35
|
+
labels: {
|
|
36
|
+
toLight: (themeToggle === true ? undefined : themeToggle.labels?.toLight) ??
|
|
37
|
+
translate('shell.toLight', 'Switch to light mode'),
|
|
38
|
+
toDark: (themeToggle === true ? undefined : themeToggle.labels?.toDark) ??
|
|
39
|
+
translate('shell.toDark', 'Switch to dark mode')
|
|
40
|
+
}
|
|
41
|
+
};
|
|
38
42
|
const toggleProp = toggle != null ? { themeToggle: toggle } : {};
|
|
39
43
|
const main = useRef(null);
|
|
40
|
-
const skipLabel = skipLinkLabel === false
|
|
41
|
-
? null
|
|
42
|
-
: skipLinkLabel ?? translate('shell.skip', 'Skip to content');
|
|
44
|
+
const skipLabel = skipLinkLabel === false ? null : (skipLinkLabel ?? translate('shell.skip', 'Skip to content'));
|
|
43
45
|
// Focus is moved by hand rather than left to the fragment navigation `href="#main"` would
|
|
44
46
|
// perform: that changes the location, and the router reads a location change as navigation.
|
|
45
47
|
// The `href` stays so the link is a link — focusable, announced, and working without script.
|
|
@@ -47,7 +49,7 @@ export const NavLayout = ({ nav, translate = navLabelHelper.defaultNavTranslate,
|
|
|
47
49
|
event.preventDefault();
|
|
48
50
|
main.current?.focus();
|
|
49
51
|
};
|
|
50
|
-
const brandAlias = home ?? nav.sections.find(section => section.items.length > 0)?.items[0]?.alias;
|
|
52
|
+
const brandAlias = home ?? nav.sections.find((section) => section.items.length > 0)?.items[0]?.alias;
|
|
51
53
|
// MERGED over the default, never substituted for it. `containerClassName` is how a design
|
|
52
54
|
// adjusts ONE aspect of the rhythm — almost always the width — and a caller passing
|
|
53
55
|
// `max-w-[1280px]` means "wider", not "no padding and no centring". Substituting dropped
|
|
@@ -56,10 +58,6 @@ export const NavLayout = ({ nav, translate = navLabelHelper.defaultNavTranslate,
|
|
|
56
58
|
// winning on the utility it names and leaves the rest of the rhythm standing, so a width-only
|
|
57
59
|
// value stays a width-only change; `px-8` still overrides the padding when that is the intent.
|
|
58
60
|
const container = cn(CONTAINER, containerClassName);
|
|
59
|
-
return _jsxs("div", { className: cn('flex min-h-screen flex-col bg-background text-foreground', className), style: style, children: [skipLabel != null && _jsx("a", { href: "#main", onClick: skip, "data-skip-link": true, className: SKIP_LINK, children: skipLabel }), _jsxs("header", { className: cn('sticky top-0 z-40 isolate border-b bg-background text-foreground', headerClassName), children: [_jsxs("div", { className: cn('flex h-14 items-center gap-6', container), children: [title != null ? _jsx("a", { onClick: brandAlias != null ? navigator.press(brandAlias) : undefined, className: cn('brand flex items-center gap-2 text-lg font-semibold', brandAlias != null && 'cursor-pointer'), children: title }) : null, _jsx(TopNav, { config: nav, translate: translate, ariaLabel: "Sections", className: mobileMenu ? 'hidden md:flex' : undefined }), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [actions, mobileMenu && _jsx(MobileNav, { config: nav, translate: translate, ariaLabel: "Sections", className: "md:hidden" })] })] }), !mobileMenu && _jsx(SideNav, { config: nav, translate: translate, variant: "bar", ariaLabel: "Screens", className: "md:hidden" }), _jsx("div", { "aria-hidden": "true", "data-nav-backdrop": true, className: "absolute inset-0 -z-10 bg-background pointer-events-none" })] }), _jsxs("div", { className: "flex flex-1", children: [_jsx(SideNav, { config: nav, translate: translate, variant: "side", ariaLabel: "Screens", className: "hidden md:block" }), _jsx("main", { ref: main, ...(skipLabel != null ? { id: 'main', tabIndex: -1 } : {}), className: cn('flex-1 py-8 outline-none', contentClassName), children: _jsx("div", { className: cn(container), children: children }) })] }), Array.isArray(footer)
|
|
60
|
-
? _jsx(Footer, { links: footer, translate: translate, containerClassName: container, ...toggleProp })
|
|
61
|
-
: footer != null
|
|
62
|
-
? _jsx(Footer, { containerClassName: container, content: footer, ...toggleProp })
|
|
63
|
-
: _jsx(Footer, { containerClassName: container, ...toggleProp })] });
|
|
61
|
+
return (_jsxs("div", { className: cn('flex min-h-screen flex-col bg-background text-foreground', className), style: style, children: [skipLabel != null && (_jsx("a", { href: "#main", onClick: skip, "data-skip-link": true, className: SKIP_LINK, children: skipLabel })), _jsxs("header", { className: cn('sticky top-0 z-40 isolate border-b bg-background text-foreground', headerClassName), children: [_jsxs("div", { className: cn('flex h-14 items-center gap-6', container), children: [title != null ? (_jsx("a", { onClick: brandAlias != null ? navigator.press(brandAlias) : undefined, className: cn('brand flex items-center gap-2 text-lg font-semibold', brandAlias != null && 'cursor-pointer'), children: title })) : null, (!mobileMenu || wide) && (_jsx(TopNav, { config: nav, translate: translate, ariaLabel: "Sections", testId: navTestId, className: mobileMenu ? 'hidden md:flex' : undefined })), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [actions, mobileMenu && !wide && (_jsx(MobileNav, { config: nav, translate: translate, ariaLabel: "Sections", className: "md:hidden" }))] })] }), !mobileMenu && (_jsx(SideNav, { config: nav, translate: translate, variant: "bar", ariaLabel: "Screens", className: "md:hidden" })), _jsx("div", { "aria-hidden": "true", "data-nav-backdrop": true, className: "absolute inset-0 -z-10 bg-background pointer-events-none" })] }), _jsxs("div", { className: "flex flex-1", children: [_jsx(SideNav, { config: nav, translate: translate, variant: "side", ariaLabel: "Screens", className: "hidden md:block" }), _jsx("main", { ref: main, ...(skipLabel != null ? { id: 'main', tabIndex: -1 } : {}), className: cn('flex-1 py-8 outline-none', contentClassName), children: _jsx("div", { className: cn(container), children: children }) })] }), Array.isArray(footer) ? (_jsx(Footer, { links: footer, translate: translate, containerClassName: container, ...toggleProp })) : footer != null ? (_jsx(Footer, { containerClassName: container, content: footer, ...toggleProp })) : (_jsx(Footer, { containerClassName: container, ...toggleProp }))] }));
|
|
64
62
|
};
|
|
65
63
|
//# sourceMappingURL=layout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/components/nav/layout.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAA4B,MAAM,OAAO,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAExD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,SAAS,GAAuB,CAAC,EAC5C,GAAG,
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/components/nav/layout.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAA4B,MAAM,OAAO,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAE/C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAExD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,SAAS,GAAuB,CAAC,EAC5C,GAAG,EACH,SAAS,GAAG,cAAc,CAAC,mBAAmB,EAC9C,KAAK,EACL,IAAI,EACJ,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,EACT,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,GAAG,KAAK,EAClB,aAAa,EACb,WAAW,EACX,SAAS,EACV,EAAE,EAAE;IACH,MAAM,SAAS,GAAG,WAAW,EAAE,CAAA;IAC/B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;IACzB,+FAA+F;IAC/F,4FAA4F;IAC5F,MAAM,MAAM,GACV,WAAW,IAAI,IAAI,IAAI,WAAW,KAAK,KAAK;QAC1C,CAAC,CAAC,SAAS;QACX,CAAC,CAAC;YACE,MAAM,EAAE;gBACN,OAAO,EACL,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;oBAChE,SAAS,CAAC,eAAe,EAAE,sBAAsB,CAAC;gBACpD,MAAM,EACJ,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;oBAC/D,SAAS,CAAC,cAAc,EAAE,qBAAqB,CAAC;aACnD;SACF,CAAA;IACP,MAAM,UAAU,GAAG,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAChE,MAAM,IAAI,GAAG,MAAM,CAAc,IAAI,CAAC,CAAA;IACtC,MAAM,SAAS,GAAG,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,IAAI,SAAS,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,CAAA;IAChH,0FAA0F;IAC1F,4FAA4F;IAC5F,6FAA6F;IAC7F,MAAM,IAAI,GAAG,CAAC,KAAoC,EAAE,EAAE;QACpD,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;IACvB,CAAC,CAAA;IACD,MAAM,UAAU,GAAG,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAA;IACpG,0FAA0F;IAC1F,oFAAoF;IACpF,yFAAyF;IACzF,uFAAuF;IACvF,yFAAyF;IACzF,8FAA8F;IAC9F,+FAA+F;IAC/F,MAAM,SAAS,GAAG,EAAE,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAA;IAEnD,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,0DAA0D,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,aAKpG,SAAS,IAAI,IAAI,IAAI,CACpB,YAAG,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,IAAI,0BAAiB,SAAS,EAAE,SAAS,YAC/D,SAAS,GACR,CACL,EA0BD,kBAAQ,SAAS,EAAE,EAAE,CAAC,kEAAkE,EAAE,eAAe,CAAC,aACxG,eAAK,SAAS,EAAE,EAAE,CAAC,8BAA8B,EAAE,SAAS,CAAC,aAC1D,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CACf,YACE,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EACrE,SAAS,EAAE,EAAE,CACX,qDAAqD,EACrD,UAAU,IAAI,IAAI,IAAI,gBAAgB,CACvC,YAEA,KAAK,GACJ,CACL,CAAC,CAAC,CAAC,IAAI,EACP,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CACxB,KAAC,MAAM,IACL,MAAM,EAAE,GAAG,EACX,SAAS,EAAE,SAAS,EACpB,SAAS,EAAC,UAAU,EACpB,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,GACpD,CACH,EAMD,eAAK,SAAS,EAAC,iCAAiC,aAC7C,OAAO,EACP,UAAU,IAAI,CAAC,IAAI,IAAI,CACtB,KAAC,SAAS,IAAC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,UAAU,EAAC,SAAS,EAAC,WAAW,GAAG,CAC5F,IACG,IACF,EACL,CAAC,UAAU,IAAI,CACd,KAAC,OAAO,IAAC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAC,KAAK,EAAC,SAAS,EAAC,SAAS,EAAC,SAAS,EAAC,WAAW,GAAG,CACvG,EASD,6BACc,MAAM,6BAElB,SAAS,EAAC,0DAA0D,GACpE,IACK,EACT,eAAK,SAAS,EAAC,aAAa,aAC1B,KAAC,OAAO,IAAC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAC,MAAM,EAAC,SAAS,EAAC,SAAS,EAAC,SAAS,EAAC,iBAAiB,GAAG,EAM7G,eACE,GAAG,EAAE,IAAI,KACL,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAC3D,SAAS,EAAE,EAAE,CAAC,0BAA0B,EAAE,gBAAgB,CAAC,YAE3D,cAAK,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,YAAG,QAAQ,GAAO,GAC1C,IACH,EAWL,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CACvB,KAAC,MAAM,IAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,KAAM,UAAU,GAAI,CAC/F,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CACnB,KAAC,MAAM,IAAC,kBAAkB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAM,UAAU,GAAI,CAC3E,CAAC,CAAC,CAAC,CACF,KAAC,MAAM,IAAC,kBAAkB,EAAE,SAAS,KAAM,UAAU,GAAI,CAC1D,IACG,CACP,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../../../src/components/nav/mobile.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,EAAE,EAAmB,MAAM,OAAO,CAAA;AAOjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../../../src/components/nav/mobile.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,EAAE,EAAmB,MAAM,OAAO,CAAA;AAOjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAyIxC,CAAA"}
|
|
@@ -39,17 +39,17 @@ export const MobileNav = ({ config, translate = navLabelHelper.defaultNavTransla
|
|
|
39
39
|
setOpen(false);
|
|
40
40
|
go();
|
|
41
41
|
};
|
|
42
|
-
return _jsxs(Sheet, { open: open, onOpenChange: setOpen, children: [_jsx(SheetTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": label, "data-nav-menu-trigger": true, className: cn(TARGET, className), style: style, children: _jsx(MenuIcon, { className: "size-6", "aria-hidden": "true" }) }) }), _jsxs(SheetContent, { side: "right", showCloseButton: false, "aria-describedby": undefined, "data-nav-sheet": true, className: "gap-0 p-0 text-foreground", children: [_jsxs("div", { className: "flex h-14 shrink-0 items-center justify-between border-b pl-4 pr-2", children: [_jsx(SheetTitle, { className: "text-base", children: label }), _jsx(SheetClose, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": closeLabel, className: TARGET, children: _jsx(XIcon, { className: "size-5", "aria-hidden": "true" }) }) })] }), _jsx("nav", { "aria-label": ariaLabel, className: "flex-1 overflow-y-auto p-2", children: _jsx("ul", { className: "flex flex-col gap-1", children: model.sections.map(section => {
|
|
42
|
+
return (_jsxs(Sheet, { open: open, onOpenChange: setOpen, children: [_jsx(SheetTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": label, "data-nav-menu-trigger": true, className: cn(TARGET, className), style: style, children: _jsx(MenuIcon, { className: "size-6", "aria-hidden": "true" }) }) }), _jsxs(SheetContent, { side: "right", showCloseButton: false, "aria-describedby": undefined, "data-nav-sheet": true, className: "gap-0 p-0 text-foreground", children: [_jsxs("div", { className: "flex h-14 shrink-0 items-center justify-between border-b pl-4 pr-2", children: [_jsx(SheetTitle, { className: "text-base", children: label }), _jsx(SheetClose, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": closeLabel, className: TARGET, children: _jsx(XIcon, { className: "size-5", "aria-hidden": "true" }) }) })] }), _jsx("nav", { "aria-label": ariaLabel, className: "flex-1 overflow-y-auto p-2", children: _jsx("ul", { className: "flex flex-col gap-1", children: model.sections.map((section) => {
|
|
43
43
|
const sectionLabel = navLabelHelper.resolveNavLabel(translate, section.label, `nav.${section.name}`, section.name);
|
|
44
44
|
if (section.items.length > 1) {
|
|
45
45
|
const heading = `${id}-${section.name}`;
|
|
46
|
-
return _jsxs("li", { className: "pt-2", children: [_jsx("p", { id: heading, className: cn('px-3 pb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground', model.isSectionActive(section) && 'text-foreground'), children: sectionLabel }), _jsx("ul", { "aria-labelledby": heading, className: "flex flex-col gap-1", children: section.items.map(item => {
|
|
46
|
+
return (_jsxs("li", { className: "pt-2", children: [_jsx("p", { id: heading, className: cn('px-3 pb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground', model.isSectionActive(section) && 'text-foreground'), children: sectionLabel }), _jsx("ul", { "aria-labelledby": heading, className: "flex flex-col gap-1", children: section.items.map((item) => {
|
|
47
47
|
const active = model.isItemActive(item);
|
|
48
|
-
return _jsx("li", { children: _jsxs("a", { href: model.hrefOf(item), onClick: follow(model.goItem(item)), "aria-current": active ? 'page' : undefined, className: cn(ENTRY, active && ENTRY_ACTIVE), children: [item.Icon != null ? _jsx(item.Icon, { className: "size-4" }) : null, navLabelHelper.resolveNavLabel(translate, item.label, `modules.${item.alias}`, item.alias)] }) }, item.alias);
|
|
49
|
-
}) })] }, section.name);
|
|
48
|
+
return (_jsx("li", { children: _jsxs("a", { "data-testid": item.testId, href: model.hrefOf(item), onClick: follow(model.goItem(item)), "aria-current": active ? 'page' : undefined, className: cn(ENTRY, active && ENTRY_ACTIVE), children: [item.Icon != null ? _jsx(item.Icon, { className: "size-4" }) : null, navLabelHelper.resolveNavLabel(translate, item.label, `modules.${item.alias}`, item.alias)] }) }, item.alias));
|
|
49
|
+
}) })] }, section.name));
|
|
50
50
|
}
|
|
51
51
|
const active = model.isSectionActive(section);
|
|
52
|
-
return _jsx("li", { children: _jsx("a", { href: model.hrefOf(section), onClick: follow(model.goSection(section)), "aria-current": active ? 'page' : undefined, className: cn(ENTRY, active && ENTRY_ACTIVE), children: sectionLabel }) }, section.name);
|
|
53
|
-
}) }) })] })] });
|
|
52
|
+
return (_jsx("li", { children: _jsx("a", { "data-testid": section.testId, href: model.hrefOf(section), onClick: follow(model.goSection(section)), "aria-current": active ? 'page' : undefined, className: cn(ENTRY, active && ENTRY_ACTIVE), children: sectionLabel }) }, section.name));
|
|
53
|
+
}) }) })] })] }));
|
|
54
54
|
};
|
|
55
55
|
//# sourceMappingURL=mobile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile.js","sourceRoot":"","sources":["../../../src/components/nav/mobile.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAA4B,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAG1G,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAuB,
|
|
1
|
+
{"version":3,"file":"mobile.js","sourceRoot":"","sources":["../../../src/components/nav/mobile.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"side.d.ts","sourceRoot":"","sources":["../../../src/components/nav/side.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"side.d.ts","sourceRoot":"","sources":["../../../src/components/nav/side.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAM/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,EAAE,CAAC,YAAY,CAoDpC,CAAA"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { usePanelNav, navLabelHelper } from '@owlmeans/client-panel';
|
|
3
3
|
import { cn } from '../../@/lib/utils.js';
|
|
4
4
|
import { Button } from '../../@/components/ui/button.js';
|
|
5
|
+
import { useWideNav } from './viewport.js';
|
|
5
6
|
/**
|
|
6
7
|
* The second navigation level — the screens of the active section.
|
|
7
8
|
*
|
|
@@ -11,15 +12,14 @@ import { Button } from '../../@/components/ui/button.js';
|
|
|
11
12
|
*/
|
|
12
13
|
export const SideNav = ({ config, translate = navLabelHelper.defaultNavTranslate, variant = 'side', ariaLabel, className, style }) => {
|
|
13
14
|
const model = usePanelNav(config);
|
|
15
|
+
const wide = useWideNav();
|
|
14
16
|
if (!model.showSide || model.active == null) {
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
17
|
-
const items = model.active.items.map(item => {
|
|
19
|
+
const items = model.active.items.map((item) => {
|
|
18
20
|
const label = navLabelHelper.resolveNavLabel(translate, item.label, `modules.${item.alias}`, item.alias);
|
|
19
|
-
return _jsxs(Button, { variant: "ghost", size: variant === 'bar' ? 'sm' : 'default', onClick: model.goItem(item), "aria-current": model.isItemActive(item) ? 'page' : undefined, className: cn(variant === 'bar' ? 'shrink-0' : 'w-full justify-start', model.isItemActive(item) && 'bg-accent text-accent-foreground'), children: [item.Icon != null ? _jsx(item.Icon, { className: "size-4" }) : null, label] }, item.alias);
|
|
21
|
+
return (_jsxs(Button, { "data-testid": (variant === 'side' ? wide : !wide) ? item.testId : undefined, variant: "ghost", size: variant === 'bar' ? 'sm' : 'default', onClick: model.goItem(item), "aria-current": model.isItemActive(item) ? 'page' : undefined, className: cn(variant === 'bar' ? 'shrink-0' : 'w-full justify-start', model.isItemActive(item) && 'bg-accent text-accent-foreground'), children: [item.Icon != null ? _jsx(item.Icon, { className: "size-4" }) : null, label] }, item.alias));
|
|
20
22
|
});
|
|
21
|
-
return variant === 'bar'
|
|
22
|
-
? _jsx("nav", { "aria-label": ariaLabel, className: cn('flex gap-1 overflow-x-auto border-b px-2 py-1', className), style: style, children: items })
|
|
23
|
-
: _jsx("aside", { className: cn('w-56 shrink-0 border-r', className), style: style, children: _jsx("nav", { "aria-label": ariaLabel, className: "flex flex-col gap-1 p-3", children: items }) });
|
|
23
|
+
return variant === 'bar' ? (_jsx("nav", { "aria-label": ariaLabel, className: cn('flex gap-1 overflow-x-auto border-b px-2 py-1', className), style: style, children: items })) : (_jsx("aside", { className: cn('w-56 shrink-0 border-r', className), style: style, children: _jsx("nav", { "aria-label": ariaLabel, className: "flex flex-col gap-1 p-3", children: items }) }));
|
|
24
24
|
};
|
|
25
25
|
//# sourceMappingURL=side.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"side.js","sourceRoot":"","sources":["../../../src/components/nav/side.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"side.js","sourceRoot":"","sources":["../../../src/components/nav/side.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAG1C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAqB,CAAC,EACxC,MAAM,EACN,SAAS,GAAG,cAAc,CAAC,mBAAmB,EAC9C,OAAO,GAAG,MAAM,EAChB,SAAS,EACT,SAAS,EACT,KAAK,EACN,EAAE,EAAE;IACH,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;IAEzB,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5C,MAAM,KAAK,GAAG,cAAc,CAAC,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAExG,OAAO,CACL,MAAC,MAAM,mBAEQ,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC1E,OAAO,EAAC,OAAO,EACf,IAAI,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAC1C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,kBACb,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC3D,SAAS,EAAE,EAAE,CACX,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,sBAAsB,EACvD,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,kCAAkC,CAC/D,aAEA,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAC,IAAI,CAAC,IAAI,IAAC,SAAS,EAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,EAC3D,KAAK,KAZD,IAAI,CAAC,KAAK,CAaR,CACV,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CACzB,4BACc,SAAS,EACrB,SAAS,EAAE,EAAE,CAAC,+CAA+C,EAAE,SAAS,CAAC,EACzE,KAAK,EAAE,KAAK,YAEX,KAAK,GACF,CACP,CAAC,CAAC,CAAC,CACF,gBAAO,SAAS,EAAE,EAAE,CAAC,wBAAwB,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,YACrE,4BAAiB,SAAS,EAAE,SAAS,EAAC,yBAAyB,YAC5D,KAAK,GACF,GACA,CACT,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top.d.ts","sourceRoot":"","sources":["../../../src/components/nav/top.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"top.d.ts","sourceRoot":"","sources":["../../../src/components/nav/top.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAU/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C;;;;;GAKG;AACH,eAAO,MAAM,MAAM,EAAE,EAAE,CAAC,WAAW,CA2ClC,CAAA"}
|
|
@@ -9,17 +9,20 @@ import { SECTION_LINK } from './consts.local.js';
|
|
|
9
9
|
* Pressing a section goes to its first screen; the side menu then offers the rest. The
|
|
10
10
|
* viewport is off because no section opens a panel: these are links, not dropdowns.
|
|
11
11
|
*/
|
|
12
|
-
export const TopNav = ({ config, translate = navLabelHelper.defaultNavTranslate, ariaLabel, className, style }) => {
|
|
12
|
+
export const TopNav = ({ config, translate = navLabelHelper.defaultNavTranslate, ariaLabel, testId, className, style }) => {
|
|
13
13
|
const model = usePanelNav(config);
|
|
14
14
|
if (model.sections.length < 1) {
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
|
-
return _jsx(NavigationMenu, { viewport: false, "aria-label": ariaLabel, className: className, style: style, children: _jsx(NavigationMenuList, { className: "flex-wrap gap-4", children: model.sections.map(section => {
|
|
17
|
+
return (_jsx(NavigationMenu, { viewport: false, "aria-label": ariaLabel, "data-testid": testId, className: className, style: style, children: _jsx(NavigationMenuList, { className: "flex-wrap gap-4", children: model.sections.map((section) => {
|
|
18
18
|
const go = model.goSection(section);
|
|
19
|
-
return _jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { active: model.isSectionActive(section),
|
|
19
|
+
return (_jsx(NavigationMenuItem, { children: _jsx(NavigationMenuLink, { active: model.isSectionActive(section), "aria-current": model.isSectionActive(section) ? 'page' : undefined, "data-testid": section.testId,
|
|
20
20
|
// A real `href` keeps the entry focusable and openable in a new tab; the click is
|
|
21
21
|
// still handled in-app, so the browser never reloads the whole application.
|
|
22
|
-
href: model.hrefOf(section), onClick: event => {
|
|
23
|
-
|
|
22
|
+
href: model.hrefOf(section), onClick: (event) => {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
go();
|
|
25
|
+
}, className: cn(SECTION_LINK), children: navLabelHelper.resolveNavLabel(translate, section.label, `nav.${section.name}`, section.name) }) }, section.name));
|
|
26
|
+
}) }) }));
|
|
24
27
|
};
|
|
25
28
|
//# sourceMappingURL=top.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top.js","sourceRoot":"","sources":["../../../src/components/nav/top.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EACL,cAAc,
|
|
1
|
+
{"version":3,"file":"top.js","sourceRoot":"","sources":["../../../src/components/nav/top.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAA;AACzC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,0CAA0C,CAAA;AAGjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAoB,CAAC,EACtC,MAAM,EACN,SAAS,GAAG,cAAc,CAAC,mBAAmB,EAC9C,SAAS,EACT,MAAM,EACN,SAAS,EACT,KAAK,EACN,EAAE,EAAE;IACH,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IAEjC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,KAAC,cAAc,IAAC,QAAQ,EAAE,KAAK,gBAAc,SAAS,iBAAe,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,YAC7G,KAAC,kBAAkB,IAAC,SAAS,EAAC,iBAAiB,YAC5C,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC9B,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;gBAEnC,OAAO,CACL,KAAC,kBAAkB,cACjB,KAAC,kBAAkB,IACjB,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,kBACxB,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,iBACpD,OAAO,CAAC,MAAM;wBAC3B,kFAAkF;wBAClF,4EAA4E;wBAC5E,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAC3B,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;4BACjB,KAAK,CAAC,cAAc,EAAE,CAAA;4BACtB,EAAE,EAAE,CAAA;wBACN,CAAC,EACD,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,YAE1B,cAAc,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,GAC3E,IAfE,OAAO,CAAC,IAAI,CAgBhB,CACtB,CAAA;YACH,CAAC,CAAC,GACiB,GACN,CAClB,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -7,6 +7,7 @@ interface NavCommonProps extends StyledProps {
|
|
|
7
7
|
/** See {@link NavTranslate} — omitted, literal labels and humanized aliases are used. */
|
|
8
8
|
translate?: NavTranslate;
|
|
9
9
|
ariaLabel?: string;
|
|
10
|
+
testId?: string;
|
|
10
11
|
}
|
|
11
12
|
export interface TopNavProps extends NavCommonProps {
|
|
12
13
|
}
|
|
@@ -26,6 +27,8 @@ export interface MobileNavProps extends NavCommonProps {
|
|
|
26
27
|
}
|
|
27
28
|
export interface NavLayoutProps extends PropsWithChildren<StyledProps> {
|
|
28
29
|
nav: PanelNavConfig;
|
|
30
|
+
/** Stable selector on the desktop navigation landmark. */
|
|
31
|
+
navTestId?: string;
|
|
29
32
|
translate?: NavTranslate;
|
|
30
33
|
/** Brand slot — rendered at the far left of the header. */
|
|
31
34
|
title?: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/nav/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,UAAU,cAAe,SAAQ,WAAW;IAC1C,MAAM,EAAE,cAAc,CAAA;IACtB,yFAAyF;IACzF,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/nav/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,UAAU,cAAe,SAAQ,WAAW;IAC1C,MAAM,EAAE,cAAc,CAAA;IACtB,yFAAyF;IACzF,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,cAAc;CAAG;AAEtD,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,cAAc;CAAG;AAEzD,MAAM,WAAW,cAAe,SAAQ,iBAAiB,CAAC,WAAW,CAAC;IACpE,GAAG,EAAE,cAAc,CAAA;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yFAAyF;IACzF,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAC9B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAA;IACnC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../../../src/components/nav/viewport.ts"],"names":[],"mappings":"AAEA,yFAAyF;AACzF,eAAO,MAAM,UAAU,QAAO,OAY7B,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
/** The same md boundary as the shell's Tailwind layout, including resize after mount. */
|
|
3
|
+
export const useWideNav = () => {
|
|
4
|
+
const [wide, setWide] = useState(() => typeof window === 'undefined' || window.matchMedia('(min-width: 768px)').matches);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const media = window.matchMedia('(min-width: 768px)');
|
|
7
|
+
const change = () => setWide(media.matches);
|
|
8
|
+
change();
|
|
9
|
+
media.addEventListener('change', change);
|
|
10
|
+
return () => media.removeEventListener('change', change);
|
|
11
|
+
}, []);
|
|
12
|
+
return wide;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=viewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport.js","sourceRoot":"","sources":["../../../src/components/nav/viewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE3C,yFAAyF;AACzF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAY,EAAE;IACtC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAC9B,GAAG,EAAE,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC,OAAO,CACvF,CAAA;IACD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAA;QACrD,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC3C,MAAM,EAAE,CAAA;QACR,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACxC,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC1D,CAAC,EAAE,EAAE,CAAC,CAAA;IACN,OAAO,IAAI,CAAA;AACb,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/editor/component.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAW/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CA6J1C,CAAA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { basicSetup } from 'codemirror';
|
|
4
|
+
import { Compartment, EditorState } from '@codemirror/state';
|
|
5
|
+
import { EditorView } from '@codemirror/view';
|
|
6
|
+
import { syntaxHighlighting } from '@codemirror/language';
|
|
7
|
+
import { json, jsonParseLinter } from '@codemirror/lang-json';
|
|
8
|
+
import { linter } from '@codemirror/lint';
|
|
9
|
+
import { Braces, Check, Copy, LockKeyhole, WrapText } from 'lucide-react';
|
|
10
|
+
import { Button } from '../@/components/ui/button.js';
|
|
11
|
+
import { cn } from '../@/lib/utils.js';
|
|
12
|
+
import { EDITOR_THEME, JSON_HIGHLIGHT, LABELS } from './consts.local.js';
|
|
13
|
+
export const JsonEditor = ({ value, onChange, onValidityChange, readOnly = false, id, ariaLabel = 'JSON', testId, className, minHeight = 120, maxHeight = 560, labels }) => {
|
|
14
|
+
const copy = { ...LABELS, ...labels };
|
|
15
|
+
const errorId = useId();
|
|
16
|
+
const mount = useRef(null);
|
|
17
|
+
const editor = useRef(null);
|
|
18
|
+
const callback = useRef(onChange);
|
|
19
|
+
const external = useRef(false);
|
|
20
|
+
const [access] = useState(() => new Compartment());
|
|
21
|
+
const [attributes] = useState(() => new Compartment());
|
|
22
|
+
const [copied, setCopied] = useState(false);
|
|
23
|
+
const [copyFailed, setCopyFailed] = useState(false);
|
|
24
|
+
const error = useMemo(() => {
|
|
25
|
+
try {
|
|
26
|
+
JSON.parse(value);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
catch (cause) {
|
|
30
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
31
|
+
}
|
|
32
|
+
}, [value]);
|
|
33
|
+
callback.current = onChange;
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (mount.current == null)
|
|
36
|
+
return;
|
|
37
|
+
const view = new EditorView({
|
|
38
|
+
parent: mount.current,
|
|
39
|
+
doc: value,
|
|
40
|
+
extensions: [
|
|
41
|
+
basicSetup,
|
|
42
|
+
json(),
|
|
43
|
+
linter(jsonParseLinter()),
|
|
44
|
+
EDITOR_THEME,
|
|
45
|
+
syntaxHighlighting(JSON_HIGHLIGHT),
|
|
46
|
+
EditorView.lineWrapping,
|
|
47
|
+
access.of([EditorState.readOnly.of(readOnly), EditorView.editable.of(!readOnly)]),
|
|
48
|
+
attributes.of(EditorView.contentAttributes.of({ 'aria-label': ariaLabel })),
|
|
49
|
+
EditorView.updateListener.of((update) => {
|
|
50
|
+
if (update.docChanged && !external.current)
|
|
51
|
+
callback.current?.(update.state.doc.toString());
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
editor.current = view;
|
|
56
|
+
return () => {
|
|
57
|
+
view.destroy();
|
|
58
|
+
editor.current = null;
|
|
59
|
+
};
|
|
60
|
+
}, []);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const view = editor.current;
|
|
63
|
+
if (view == null || view.state.doc.toString() === value)
|
|
64
|
+
return;
|
|
65
|
+
external.current = true;
|
|
66
|
+
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: value } });
|
|
67
|
+
external.current = false;
|
|
68
|
+
}, [value]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
editor.current?.dispatch({
|
|
71
|
+
effects: [
|
|
72
|
+
access.reconfigure([EditorState.readOnly.of(readOnly), EditorView.editable.of(!readOnly)]),
|
|
73
|
+
attributes.reconfigure(EditorView.contentAttributes.of({
|
|
74
|
+
...(id != null ? { id } : {}),
|
|
75
|
+
...(testId != null ? { 'data-testid': testId } : {}),
|
|
76
|
+
'aria-label': ariaLabel,
|
|
77
|
+
'aria-readonly': String(readOnly),
|
|
78
|
+
'aria-invalid': String(error != null),
|
|
79
|
+
...(error != null ? { 'aria-describedby': errorId } : {})
|
|
80
|
+
}))
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
}, [readOnly, id, testId, ariaLabel, error]);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
onValidityChange?.(error == null);
|
|
86
|
+
}, [error, onValidityChange]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
setCopied(false);
|
|
89
|
+
setCopyFailed(false);
|
|
90
|
+
}, [value]);
|
|
91
|
+
const copyText = async () => {
|
|
92
|
+
try {
|
|
93
|
+
await navigator.clipboard.writeText(value);
|
|
94
|
+
setCopied(true);
|
|
95
|
+
setCopyFailed(false);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
setCopyFailed(true);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
return (_jsxs("div", { "data-json-editor": true, "data-valid": error == null, className: cn('min-w-0 overflow-hidden rounded-lg border bg-background text-foreground shadow-xs focus-within:ring-2 focus-within:ring-ring/40', error != null && 'border-destructive', className), children: [_jsxs("div", { className: "flex min-h-10 flex-wrap items-center justify-between gap-2 border-b bg-muted/50 px-3 py-1", children: [_jsxs("span", { className: "inline-flex items-center gap-2 font-mono text-xs text-muted-foreground", children: [_jsx(Braces, { className: "size-3.5", "aria-hidden": "true" }), "JSON", readOnly && (_jsxs("span", { className: "inline-flex items-center gap-1 font-sans", children: [_jsx(LockKeyhole, { className: "size-3" }), copy.readOnly] }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [!readOnly && (_jsxs(Button, { type: "button", size: "sm", variant: "ghost", disabled: error != null, onClick: () => onChange?.(JSON.stringify(JSON.parse(value), null, 2)), children: [_jsx(WrapText, { className: "size-3.5" }), copy.format] })), _jsxs(Button, { type: "button", size: "sm", variant: "ghost", onClick: () => void copyText(), children: [copied ? _jsx(Check, { className: "size-3.5" }) : _jsx(Copy, { className: "size-3.5" }), copied ? copy.copied : copyFailed ? copy.copyFailed : copy.copy] })] })] }), _jsx("div", { ref: mount, className: "[&_.cm-scroller]:max-h-[var(--json-max-height)] [&_.cm-scroller]:min-h-[var(--json-min-height)]", style: { '--json-min-height': `${minHeight}px`, '--json-max-height': `${maxHeight}px` } }), _jsx("div", { className: cn('border-t px-3 py-1.5 text-xs', error != null ? 'text-destructive' : 'text-muted-foreground'), "aria-live": "polite", id: errorId, children: error != null ? `${copy.invalid}: ${error}` : copy.valid })] }));
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=component.js.map
|