@espressif/dashboard-ui-components 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -110,6 +110,37 @@ Things agents get wrong about it:
110
110
  full page reload.
111
111
  - No `prose` wrapper or spacing utilities are needed; it owns its own vertical rhythm.
112
112
 
113
+ ## Editing code and JSON
114
+
115
+ `CodeEditor` (from `/components`) is the editable counterpart of `Code`: a form field
116
+ with syntax highlighting. Use it instead of `Textarea` for JSON payloads, policies or
117
+ templates; use `Code` for read-only display.
118
+
119
+ ```tsx
120
+ import { CodeEditor } from '@espressif/dashboard-ui-components/components'
121
+
122
+ <CodeEditor
123
+ {...field} // react-hook-form: native onChange + ref to the <textarea>
124
+ lang="json" // "text" for base64 / opaque data
125
+ theme={dark ? 'dark' : 'light'}
126
+ label="Payload"
127
+ errorContent={fieldState.error?.message}
128
+ onFormatError={(e) => setFormatError(String(e))}
129
+ />
130
+ ```
131
+
132
+ Things agents get wrong about it:
133
+
134
+ - `onChange` receives the native change event, like `Textarea` — there is no `onValueChange`.
135
+ - It does **not** validate. The built-in Format button (JSON only) pretty-prints and
136
+ reports invalid JSON to `onFormatError`; schema rules (zod etc.) stay in the app.
137
+ Pass `formatter` for other languages; do not reimplement Format in `headerActions`.
138
+ - Esc, then Tab, moves focus out (Tab otherwise indents). Inside a `Sheet` or `Dialog`,
139
+ Esc also reaches the overlay's own Escape handler and closes it; guard with
140
+ `onEscapeKeyDown={(e) => { if ((e.target as Element).closest('[data-slot="code-editor"]')) e.preventDefault() }}`
141
+ if that matters.
142
+ - `className` / `style` style the editor box, not the `<textarea>`.
143
+
113
144
  ## Bundle size and tooling (what agents should assume)
114
145
 
115
146
  - The published library is **pre-bundled ESM** (tsup). Consumers receive **large `dist` chunks**, not one file per source component.
@@ -107,6 +107,37 @@ Things agents get wrong about it:
107
107
  full page reload.
108
108
  - No `prose` wrapper or spacing utilities are needed; it owns its own vertical rhythm.
109
109
 
110
+ ## Editing code and JSON
111
+
112
+ `CodeEditor` (from `/components`) is the editable counterpart of `Code`: a form field
113
+ with syntax highlighting. Use it instead of `Textarea` for JSON payloads, policies or
114
+ templates; use `Code` for read-only display.
115
+
116
+ ```tsx
117
+ import { CodeEditor } from '@espressif/dashboard-ui-components/components'
118
+
119
+ <CodeEditor
120
+ {...field} // react-hook-form: native onChange + ref to the <textarea>
121
+ lang="json" // "text" for base64 / opaque data
122
+ theme={dark ? 'dark' : 'light'}
123
+ label="Payload"
124
+ errorContent={fieldState.error?.message}
125
+ onFormatError={(e) => setFormatError(String(e))}
126
+ />
127
+ ```
128
+
129
+ Things agents get wrong about it:
130
+
131
+ - `onChange` receives the native change event, like `Textarea` — there is no `onValueChange`.
132
+ - It does **not** validate. The built-in Format button (JSON only) pretty-prints and
133
+ reports invalid JSON to `onFormatError`; schema rules (zod etc.) stay in the app.
134
+ Pass `formatter` for other languages; do not reimplement Format in `headerActions`.
135
+ - Esc, then Tab, moves focus out (Tab otherwise indents). Inside a `Sheet` or `Dialog`,
136
+ Esc also reaches the overlay's own Escape handler and closes it; guard with
137
+ `onEscapeKeyDown={(e) => { if ((e.target as Element).closest('[data-slot="code-editor"]')) e.preventDefault() }}`
138
+ if that matters.
139
+ - `className` / `style` style the editor box, not the `<textarea>`.
140
+
110
141
  ## Bundle size and tooling (what agents should assume)
111
142
 
112
143
  - The published library is **pre-bundled ESM** (tsup). Consumers receive **large `dist` chunks**, not one file per source component.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,203 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres
5
5
  to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.6.0] - 2026-09-25
8
+
9
+ A minor release adding an editable code field (`CodeEditor`), a warning
10
+ state to `StatusCardList`, fixed-value fields to `KeyValueManager`, and
11
+ rolling presets plus a clear action to `DateRangePicker`. Focus rings now
12
+ follow the primary colour, and a render loop in `DataTable` is fixed. No
13
+ breaking changes.
14
+
15
+ ### Added
16
+ - **`CodeEditor`** — an editable, syntax-highlighted code field. It has the
17
+ same header, file name, copy button and Shiki themes as `Code`, and works
18
+ in a form like `Textarea`: controlled or uncontrolled, native `onChange` /
19
+ `onBlur` / `name`, a ref to the `<textarea>`, so `{...field}` and
20
+ `{...register(name)}` from react-hook-form work unchanged (including
21
+ values written by `setValue` / `reset`). Carries `label`, `required`,
22
+ `hintContent`, `error`, `errorContent`, `disabled`, `readOnly` and
23
+ `placeholder`.
24
+ - Tab indents (every line of a multi-line selection), Shift+Tab outdents,
25
+ Enter keeps the current indentation, and all three are undoable with
26
+ Cmd/Ctrl+Z. Press Esc, then Tab, to move focus out of the editor. Keys
27
+ pressed during IME composition are left alone.
28
+ - For `lang="json"` the header has a Format button that pretty-prints with
29
+ `tabSize` spaces (undoable, fires `onChange`). Invalid JSON is left
30
+ unchanged and passed to `onFormatError`. `formatter` supplies a
31
+ formatter for any language; `formatButton={false}` hides the button;
32
+ `formatLabel` sets its accessible name, and `copyLabel` sets the copy
33
+ button's. `headerActions` adds further
34
+ header controls.
35
+ - `hideHeader` removes the header. It is also left out automatically when
36
+ there is no `fileName`, no `headerActions`, and neither a copy nor a
37
+ format button.
38
+ - Other props: `lang` (default `"json"`; `"text"` for base64 or other
39
+ opaque data), `theme` / `themes`, `fileName`, `icon`, `copyButton`,
40
+ `minHeight` / `maxHeight` of the editing area, `wrap` (soft-wrap long
41
+ lines; off by default) and `tabSize` (default 2).
42
+ - Above `maxHighlightLength` characters (default 10 000) the editor shows
43
+ plain text, so typing in a large document stays responsive. Highlighting
44
+ one keystroke of a 64 KB JSON document takes about 64 ms on an Apple M4,
45
+ and a 10 KB one about 13 ms. Languages or themes outside the curated
46
+ Shiki bundle also render as plain text, with one console warning per
47
+ language.
48
+ - Text renders at 16px below the `md` breakpoint so iOS Safari does not
49
+ zoom on focus, and at 13px (like `Code`) from `md` up.
50
+ - **`DateRangePicker`** — rolling presets and a clear action:
51
+ - New presets `last-1-hour`, `last-24-hours`, `past-7-days` and
52
+ `past-30-days`. They end at the moment they are selected instead of
53
+ snapping to day boundaries, so `past-7-days` and `past-30-days` include
54
+ today (the existing `last-7-days` and `last-30-days` end yesterday at
55
+ 23:59 and are unchanged). `ROLLING_PRESET_IDS` lists them, so a consumer
56
+ can recompute the range with `computeDateRange` when it refreshes data.
57
+ - New `clearable` prop. While a preset or custom range is selected, the
58
+ menu shows a "Clear" item that calls the new `onClear` callback and
59
+ closes the menu. `clearLabel` overrides its text (default `"Clear"`).
60
+ - Code with an exhaustive `switch` over `DateRangePickerPresetId` must
61
+ handle the four new IDs.
62
+ - **`KeyValueManager`** — two new `KeyValueManagerFieldMeta` options:
63
+ - `options` renders the field in the add popover as a select limited to
64
+ those values, and rejects any other value. Use it when the allowed values
65
+ are known up front, for example the header columns of an uploaded CSV.
66
+ - `regexFlags` is passed to the `RegExp` built from `regex`, so a pattern
67
+ can use Unicode property escapes such as `\p{L}` with `"u"`. Patterns
68
+ without flags behave as before.
69
+ - New label `selectPlaceholder` (default `"Select"`) for fields with
70
+ `options`.
71
+ - **`StatusCardList`** — new `"warning"` value for `StatusCardState`, for a
72
+ step that finished but only partly succeeded (for example, a bulk job where
73
+ some items failed). It uses the `warning` colour token for the card, avatar
74
+ and text, and a `TriangleAlert` end icon. Code with an exhaustive `switch`
75
+ over `StatusCardState` must handle the new value.
76
+ - **AI agent rules** — `.claude/rules/dashboard-ui-consumption.md` and
77
+ `.cursor/rules/dashboard-ui-consumption.mdc` gain an "Editing code and JSON"
78
+ section on `CodeEditor`: when to use it instead of `Textarea` / `Code`, the
79
+ react-hook-form wiring, and the usual mistakes (there is no `onValueChange`,
80
+ it does not validate, Esc inside a `Sheet` / `Dialog`).
81
+
82
+ ### Changed
83
+ - **Focus ring colour** — `--color-ring` now defaults to `var(--color-primary)`
84
+ in light and dark mode instead of fixed blues (`#0053c2` / `#7cd9ff`).
85
+ Components that draw their focus ring with `ring-ring` (`Input`, `Select`,
86
+ `Textarea`, `SearchBox`, `AdvancedSearchBox`, `ToggleGroup`, `Tabs`,
87
+ `Switch`, `Checkbox`, `Dialog` and others) previously kept the blue ring in
88
+ apps that re-theme `primary`, while `Button` and the clickable cards already
89
+ used `ring-primary`, so focus rings in the same toolbar did not match.
90
+ - With the default theme, light mode's ring moves from `#0053c2` to the
91
+ primary `#0071e3`; dark mode is unchanged (both were `#7cd9ff`).
92
+ - Apps that want a ring distinct from primary can still set `--color-ring`.
93
+ - **`DateRangePicker`** — a picker without a `presets` prop shows every
94
+ preset, so its menu now also lists the four new rolling presets. They sit
95
+ among the trailing ranges, which run shortest to longest: "Last 1 hour" and
96
+ "Last 24 hours" come before "Last 1 day", and "Past 7 days" / "Past 30 days"
97
+ come just before "Last 7 days" / "Last 30 days". `PRESET_IDS` follows the
98
+ same order. Pass `presets` to keep the previous menu.
99
+ - **`Code`** — the header's copy button now has an accessible name and
100
+ tooltip (`aria-label` / `title` "Copy"); before, the icon-only button had
101
+ no name for screen readers. The header is now shared with `CodeEditor` and
102
+ is otherwise unchanged.
103
+
104
+ ### Fixed
105
+ - **`DataTable`** — no longer triggers an extra internal re-render every time
106
+ `data` changes identity. TanStack Table resets the page index when `data`
107
+ changes unless pagination is manual, and each reset was a table-internal
108
+ state update. The page index here is always 0 (paging belongs to the
109
+ consumer through `pageIndex` / `onNextPage` / `onPrevPage`), so the reset
110
+ did nothing useful. With a consumer passing a fresh array on every render
111
+ (`data?.items ?? []`), those updates could pile up into React's "Maximum
112
+ update depth exceeded" under load, for example when switching to a
113
+ server-paginated table while the main thread was busy. The table now sets
114
+ `autoResetPageIndex: false` and memoizes its pagination state. Rows are
115
+ still capped at `pageSize`, so existing tables render the same.
116
+
117
+ ## [1.5.0] - 2026-09-22
118
+
119
+ A minor release adding a combined date and time control.
120
+
121
+ ### Added
122
+ - **`DateTimePicker`** — one read-only input that opens a dropdown with a
123
+ `Calendar` and a `TimePicker`, together editing one `Date`. Picking a day
124
+ keeps the dropdown open so the time can be set in the same interaction; a
125
+ time typed before a day is kept and applied to the chosen day; an incomplete
126
+ time (the browser reports `""` mid-edit) never snaps the value. `defaultTime`
127
+ sets the time of day used when a day is picked first (default `"00:00"`),
128
+ `timeStep` below 60 shows seconds, `clearable` shows an X inside the input
129
+ that clears both parts without changing the layout. Carries the usual
130
+ `label` / `required` / `error` / `errorContent` / `hintContent` / `size` /
131
+ `rounded` / `disabled` / `open` / `onOpenChange` props and an optional
132
+ hidden `name` input with the ISO string, like `DatePicker`. Values are local
133
+ wall-clock `Date`s; callers convert to UTC or epoch seconds.
134
+ - **`SelectTrigger`** — `label`, `required`, `hintContent`, `error` and
135
+ `errorContent`, the same field chrome `Input` has: a label row above the
136
+ trigger (label, required marker, right-aligned hint) and an error line
137
+ below it, with `aria-required` / `aria-invalid` / `aria-describedby` wired.
138
+ With none of them set the trigger renders exactly as before, so existing
139
+ `FormLabel` + `Select` compositions are unaffected.
140
+ - **`SectionCard`** — `open` for a controlled collapsible state, paired with
141
+ the existing `onOpenChange`. Lets an action in the header (an "Edit"
142
+ button, for instance) expand the card. Omit it and the card keeps its
143
+ `defaultOpen`-seeded internal state exactly as before. `hideCollapseIcon`
144
+ drops the chevron from the header's right column while the header row
145
+ itself stays a labelled toggle, for cards whose action button already
146
+ says the card can be expanded.
147
+ - **`Illustration`** — `animated` (default `false`). Off, the component
148
+ renders an illustration's static resting frame and strips every SMIL
149
+ element from the markup, so pages that only need a picture get exactly
150
+ that. On, the animation plays. Sources mark parts that exist in only one
151
+ variant with `data-variant="static"` / `data-variant="animated"` groups.
152
+ Illustrations without animations render identically either way.
153
+ - **`DatePicker`** — `clearable` and `clearLabel`. When `clearable` is set
154
+ and a date is selected, a 16px X appears inside the trigger next to the
155
+ calendar icon (the same end slot `DateTimePicker` uses) and calls
156
+ `onChange(undefined)` without opening the calendar. Off by default, so existing pickers are unchanged
157
+ (previously the only way to clear was re-clicking the selected day).
158
+
159
+ ### Changed
160
+ - **`Illustration`** — `rm-data-download` redrawn: the RainMaker logo cloud
161
+ rains data, records travel along a dashed connector into a laptop, and rows
162
+ of the on-screen table light up as they land while the download progress
163
+ fills. Colours come from the theme tokens except the cloud, which keeps the
164
+ logo's own reds. Ids are prefixed `rmdd-` so the inlined markup cannot
165
+ collide with other inline SVGs on the page. The source carries both a
166
+ static resting frame and the SMIL animation; see `animated` below.
167
+ - **`OverflowBadgeList`** — each item is now a `StatusBadge` instead of a
168
+ hand-styled `Badge`, so the pills match status chips elsewhere (rounded,
169
+ `StatusBadge` paddings, 14/16/18px icon). New `badgeSize` (`"sm" |
170
+ "default" | "lg"`, default `"default"`); `badgeVariant` still defaults to
171
+ `"outline"`. Long labels still truncate at 12rem.
172
+ - **`StatusBadge`** — the label is wrapped in a `min-w-0 truncate` span, so a
173
+ `className` with a `max-w-*` now ellipsises instead of overflowing.
174
+ - **`SelectTrigger`** — the chevron is now 16px at every `size` (it scaled
175
+ 14/16/20px before), matching the fixed 16px decorators inside `Input`, so a
176
+ Select and an Input placed side by side show the same icon dimensions.
177
+
178
+ ### Fixed
179
+ - **`Dialog`, `Sheet`, `DropdownMenu`, `Checkbox`, `Switch`, `Toggle`,
180
+ `ToggleGroup`, `Accordion`, `RadioGroup`, `PreviewCard`, `CopyButton`,
181
+ `NavBar`** — controlled mode is now genuinely controlled. The shared
182
+ `useControlledState` hook used to write its internal state on every
183
+ change and only then call `onChange`, so a component whose owner kept
184
+ `open`/`checked`/`value` unchanged still flipped on its own. A
185
+ `FullscreenDialog` with `open={true}` closed on the X button even when
186
+ its `onOpenChange` handler decided not to close (for example while a
187
+ navigation guard asked for confirmation), leaving an empty page. With
188
+ `value` defined the hook now reports that value and the setter only
189
+ notifies `onChange`; uncontrolled usage is unchanged.
190
+ - **`DatePicker`** — a controlled picker whose `value` is set back to
191
+ `undefined` no longer flips to uncontrolled mode and shows a stale day; the
192
+ component now treats the presence of the `value` prop as "controlled".
193
+ - **Field label geometry** — a `FormLabel` stacked above a control and an
194
+ `Input`'s own label row used to leave different gaps between label text and
195
+ control: `FormLabel` was an inline `<label>` sitting on a taller anonymous
196
+ line box, and the `Input` row grew from 14px to 20px whenever a hint was
197
+ present because the hint kept the default line-height. Both now render the
198
+ same 14px block-level row (`flex items-center gap-2 leading-none`; hints
199
+ are `leading-none`), so labels sit exactly 8px above every control. The
200
+ required marker also gets the same `gap-2` spacing in both.
201
+ - **`DatePicker`** — the calendar's square background no longer pokes past
202
+ the rounded corners of its dropdown; the popover now clips its content.
203
+
7
204
  ## [1.4.0] - 2026-09-18
8
205
 
9
206
  A minor release that lets `ConfirmationDialog` be opened without a trigger, and
package/README.md CHANGED
@@ -455,6 +455,7 @@ version you have installed is the shipped type declarations:
455
455
  | Alert | Alert banners (optionally dismissible) |
456
456
  | Link | Styled anchor element — colour-tinted pill, or `variant="ghost"` for a bare inline link |
457
457
  | Code | Syntax-highlighted code blocks |
458
+ | CodeEditor | Editable, syntax-highlighted code field (`Code` look, `Textarea` form behaviour), with Format (JSON) and Copy buttons |
458
459
  | MarkdownContent | Renders a markdown string with library primitives (GFM, heading anchors; raw HTML is escaped, not parsed) |
459
460
  | CopiableText / CopyButton | Copy-to-clipboard utilities |
460
461
  | ProgressBar | Progress indicator |
@@ -1,4 +1,4 @@
1
- import { Avatar, Menu, List, Button, Link } from './chunk-UHRNPXO5.js';
1
+ import { Avatar, Menu, List, Button, Link } from './chunk-ZEQNTNMW.js';
2
2
  import { cn } from './chunk-VU4CZ76T.js';
3
3
  import { motion, AnimatePresence } from 'framer-motion';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,6 +1,6 @@
1
- import { FooterCard, MyAccountMenu, ProfileCard, AppLogo } from './chunk-TSNA6BHK.js';
2
- import { useSidebar, SidebarTrigger, SidebarProvider, Sidebar2, SidebarInset, parseTextMarkup, isPathPrefixOf, isSidebarGroup } from './chunk-UHRNPXO5.js';
3
- import { Button, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator } from './chunk-7BO76CNB.js';
1
+ import { FooterCard, MyAccountMenu, ProfileCard, AppLogo } from './chunk-45DCL46L.js';
2
+ import { useSidebar, SidebarTrigger, SidebarProvider, Sidebar2, SidebarInset, parseTextMarkup, isPathPrefixOf, isSidebarGroup } from './chunk-ZEQNTNMW.js';
3
+ import { Button, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator } from './chunk-OKB2WTX7.js';
4
4
  import { cn } from './chunk-VU4CZ76T.js';
5
5
  import { Menu, Palette, Check, Globe, LogOut } from 'lucide-react';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { Separator } from './chunk-7BO76CNB.js';
1
+ import { Separator } from './chunk-OKB2WTX7.js';
2
2
  import { cn, isPresetColor, resolveColor } from './chunk-VU4CZ76T.js';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import * as React from 'react';
@@ -1,6 +1,6 @@
1
- import { ProgressBar } from './chunk-XMEZ2BP6.js';
2
- import { Card, CardContent } from './chunk-KCMVLNM5.js';
3
- import { Tooltip2 } from './chunk-UHRNPXO5.js';
1
+ import { ProgressBar } from './chunk-IYMCZTJ5.js';
2
+ import { Card, CardContent } from './chunk-ABBHGIFI.js';
3
+ import { Tooltip2 } from './chunk-ZEQNTNMW.js';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
 
6
6
  function CopyrightCard({