torch-glare 2.5.4 → 2.5.6
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/apps/lib/components/BadgeField.tsx +138 -69
- package/apps/lib/components/Button.tsx +10 -2
- package/apps/lib/components/Card.tsx +2 -1
- package/apps/lib/components/ContextMenu.tsx +65 -22
- package/apps/lib/components/DataViews/context.ts +2 -2
- package/apps/lib/components/DataViews/data-views.tsx +20 -8
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -2
- package/apps/lib/components/DataViews/index.ts +8 -4
- package/apps/lib/components/DataViews/slots.ts +9 -0
- package/apps/lib/components/DataViews/states.tsx +43 -8
- package/apps/lib/components/DataViews/views/table-view.tsx +184 -176
- package/apps/lib/components/Drawer.tsx +70 -39
- package/apps/lib/components/DropdownMenu.tsx +79 -22
- package/apps/lib/components/FormBuilder/context.ts +12 -0
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +38 -19
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +31 -8
- package/apps/lib/components/FormBuilder/submit.tsx +21 -1
- package/apps/lib/components/FormBuilder/types.ts +21 -0
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +139 -17
- package/apps/lib/components/FormRenderer/detail.tsx +57 -8
- package/apps/lib/components/FormRenderer/form-renderer.tsx +82 -10
- package/apps/lib/components/FormRenderer/index.ts +2 -0
- package/apps/lib/components/FormRenderer/notch-action.tsx +64 -0
- package/apps/lib/components/FormRenderer/stepper.tsx +56 -2
- package/apps/lib/components/FormRenderer/types.ts +37 -0
- package/apps/lib/components/HeaderBar.tsx +51 -53
- package/apps/lib/components/InputField.tsx +46 -47
- package/apps/lib/components/Popover.tsx +23 -9
- package/apps/lib/components/SearchableSelect.tsx +10 -6
- package/apps/lib/components/SearchableTree.tsx +23 -6
- package/apps/lib/components/SearchableTreeDialog.tsx +11 -1
- package/apps/lib/components/SectionBlock.tsx +24 -3
- package/apps/lib/components/Select.tsx +64 -56
- package/apps/lib/components/SlideDatePicker.tsx +5 -5
- package/apps/lib/components/TabSwitch.tsx +18 -12
- package/apps/lib/components/Table.tsx +15 -28
- package/apps/lib/hooks/useActiveTreeItem.ts +4 -1
- package/apps/lib/hooks/useHtmlDir.ts +31 -0
- package/apps/lib/hooks/useTagSelection.ts +95 -9
- package/apps/lib/layouts/FieldSection.tsx +28 -2
- package/apps/lib/registry.json +20 -5
- package/apps/lib/utils/scroller.ts +26 -0
- package/docs/components/badge-field.md +30 -4
- package/docs/components/context-menu.md +3 -1
- package/docs/components/data-views/examples/filters.md +0 -1
- package/docs/components/data-views/index.md +32 -22
- package/docs/components/data-views/migration.md +7 -5
- package/docs/components/drawer.md +5 -5
- package/docs/components/dropdown-menu.md +3 -0
- package/docs/components/form-builder.md +36 -2
- package/docs/components/form-renderer.md +71 -1
- package/docs/components/header-bar.md +3 -2
- package/docs/components/input-field.md +3 -3
- package/docs/components/section-block.md +6 -0
- package/docs/components/select.md +1 -1
- package/docs/migration/changelog.md +19 -0
- package/docs/reference/hooks.md +23 -0
- package/docs/reference/utilities.md +22 -0
- package/package.json +1 -1
- package/apps/lib/components/DataViews/filters/summary.tsx +0 -65
|
@@ -433,7 +433,7 @@ There is **no automatic per-direction styling**. The anchor is set on the root w
|
|
|
433
433
|
| Drag handle | on — `<DrawerPanel showHandle>` | off (the default) | off (the default) |
|
|
434
434
|
| Frame / tray | usually off (`framed={false}`) for clean sheet | on (default) for the dark tray | on (default) |
|
|
435
435
|
| Rounded corners | top corners only | all/left corners | top-left + bottom corners |
|
|
436
|
-
| Notch side |
|
|
436
|
+
| Notch side | inline-start (`notchSide="start"`) | inline-start | mirror to `notchSide="end"` |
|
|
437
437
|
| Best for | mobile sheets, action sheets, comments | create/edit forms, filters, detail panels | RTL panels, side navigation |
|
|
438
438
|
|
|
439
439
|
### Bottom (default)
|
|
@@ -509,7 +509,7 @@ A floating panel anchored to the right edge — the canonical home for create/ed
|
|
|
509
509
|
|
|
510
510
|
### Left (RTL / navigation)
|
|
511
511
|
|
|
512
|
-
Mirror of the right recipe: `direction="left"`, anchor to the left edge, and if you use a notch, set `notchSide="
|
|
512
|
+
Mirror of the right recipe: `direction="left"`, anchor to the left edge, and if you use a notch, set `notchSide="end"` so the tab sits on the panel's trailing edge.
|
|
513
513
|
|
|
514
514
|
```tsx
|
|
515
515
|
<Drawer direction="left">
|
|
@@ -517,7 +517,7 @@ Mirror of the right recipe: `direction="left"`, anchor to the left edge, and if
|
|
|
517
517
|
<Button variant="PrimeStyle">Open left drawer</Button>
|
|
518
518
|
</DrawerTrigger>
|
|
519
519
|
<DrawerContent
|
|
520
|
-
notchSide="
|
|
520
|
+
notchSide="end"
|
|
521
521
|
wrapperClassName="top-2 left-2 bottom-2 right-auto mt-0 h-auto w-[420px] max-w-[calc(100vw-16px)]"
|
|
522
522
|
notch={
|
|
523
523
|
<DrawerNotch>
|
|
@@ -589,7 +589,7 @@ brings its own background. This is where direction-specific styling is applied.
|
|
|
589
589
|
|---|---|---|---|
|
|
590
590
|
| `framed` | `boolean` | `true` | Show the dark "tray" frame (border + inset shadow) around the panel. Set `false` for clean bottom sheets. |
|
|
591
591
|
| `notch` | `ReactNode` | — | A `DrawerNotch` tab rendered on the top edge. |
|
|
592
|
-
| `notchSide` | `"
|
|
592
|
+
| `notchSide` | `"start" \| "end"` | `"start"` | Which **inline** edge the notch attaches to (and which corner stays square). Logical, so it mirrors under `dir="rtl"` without you computing a direction. Use `"end"` for left-anchored drawers. |
|
|
593
593
|
| `wrapperClassName` | `string` | — | Classes on the outer positioned element — this is how you anchor/size the panel per direction. |
|
|
594
594
|
| `className` | `string` | — | Classes on the **dark tray**. Add a `gap-*` here when the tray holds more than one child. |
|
|
595
595
|
| `trayClassName` | `string` | — | **Deprecated** — an alias for `className` (merged last, so it still wins). |
|
|
@@ -629,7 +629,7 @@ with each bringing its own background.
|
|
|
629
629
|
| `DrawerDescription` | Muted supporting text (maps to Vaul `Drawer.Description`). |
|
|
630
630
|
| `DrawerBadge` | Small uppercase status pill. `color`: `Blue \| Green \| Red \| Yellow \| Purple \| Gray` (default `Blue`). |
|
|
631
631
|
| `DrawerFooter` | Bottom action area (`mt-auto`, stacked). |
|
|
632
|
-
| `DrawerNotch` | The top-edge tab container. `side`: `"
|
|
632
|
+
| `DrawerNotch` | The top-edge tab container. `side`: `"start" \| "end"` (default `"start"`); normally cloned in by `DrawerContent` from `notchSide`. |
|
|
633
633
|
| `DrawerNotchClose` | Round close button for inside a notch. |
|
|
634
634
|
| `DrawerNotchPill` | Pill button for inside a notch. `color`: `Yellow \| Blue \| Gray` (default `Yellow`). Styled `<button>` only — wire navigation yourself via `onClick` (see ["What 'Open in new tab' does"](#what-open-in-new-tab-does)). |
|
|
635
635
|
| `DrawerNotchDivider` | Thin vertical divider between notch items. |
|
|
@@ -294,6 +294,8 @@ function Example() {
|
|
|
294
294
|
|
|
295
295
|
Tall menus scroll instead of overflowing off-screen. The surface caps at `maxHeight` (default `320`px) and never exceeds the space available after collision handling. Pass `maxHeight` to change the cap.
|
|
296
296
|
|
|
297
|
+
The panel itself does not scroll — it clips, and an inner viewport inside it does the scrolling. That keeps the panel's 4px frosted gutter fixed instead of scrolling away with the rows. Submenus behave identically and take their own `maxHeight`.
|
|
298
|
+
|
|
297
299
|
```typescript
|
|
298
300
|
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel } from "@/components/DropdownMenu";
|
|
299
301
|
import { Button } from "@/components/Button";
|
|
@@ -396,6 +398,7 @@ function LongMenu() {
|
|
|
396
398
|
|------|------|---------|-------------|
|
|
397
399
|
| `variant` | `'PresentationStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
398
400
|
| `autoGroup` | `boolean` | `true` | Auto-wrap loose items in boxed groups |
|
|
401
|
+
| `maxHeight` | `number` | `320` | Max height (px) before the submenu scrolls. Same `min(maxHeight, available-height)` cap as `DropdownMenuContent` |
|
|
399
402
|
| `className` | `string` | - | Additional CSS classes |
|
|
400
403
|
|
|
401
404
|
### DropdownMenuLabel
|
|
@@ -96,7 +96,33 @@ panel beside the form is `FormRenderer`'s `summary`.
|
|
|
96
96
|
## Field components
|
|
97
97
|
|
|
98
98
|
Each is a JSX child taking at least `name`, plus `label`, `placeholder`,
|
|
99
|
-
`description`, `required`, `disabled`, `hidden`, `fullWidth`.
|
|
99
|
+
`description`, `required`, `disabled`, `hidden`, `fullWidth`, `hints`.
|
|
100
|
+
|
|
101
|
+
### Hints
|
|
102
|
+
|
|
103
|
+
`hints` stacks alerts under a field — helper text, a caveat, a confirmation. Each entry mirrors
|
|
104
|
+
`FieldHint`: `label`, an optional `state` of `info` (default) / `warning` / `error` / `success`, and
|
|
105
|
+
an optional `icon`.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<FormBuilder.Text
|
|
109
|
+
name="sku"
|
|
110
|
+
label="SKU"
|
|
111
|
+
required
|
|
112
|
+
hints={[
|
|
113
|
+
{ state: "info", label: "Must be unique across the catalogue." },
|
|
114
|
+
{ state: "warning", label: "Changing this breaks existing links." },
|
|
115
|
+
]}
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The validation error is not one of these — it is rendered automatically and always comes **first**,
|
|
120
|
+
because it is the actionable message; your hints follow in the order given. A field with no `hints`
|
|
121
|
+
renders exactly as before.
|
|
122
|
+
|
|
123
|
+
Hints are ignored inside a `FormBuilder.Table` cell (and any other `bare` context): there errors
|
|
124
|
+
surface as a tooltip on the control so a row stays one line tall, and stacking alerts would defeat
|
|
125
|
+
that.
|
|
100
126
|
|
|
101
127
|
| Component | Input | Value |
|
|
102
128
|
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
@@ -110,6 +136,7 @@ Each is a JSX child taking at least `name`, plus `label`, `placeholder`,
|
|
|
110
136
|
| `FormBuilder.Select` (`options`) | `Select` | `string` |
|
|
111
137
|
| `FormBuilder.SearchableSelect` (`options`, async: `onSearchChange`/`onLoadMore`/`hasMore`) | `SearchableSelect` | `string` |
|
|
112
138
|
| `FormBuilder.MultiSelect` / `.Tags` (`options`) | `BadgeField` | `string[]` |
|
|
139
|
+
| `FormBuilder.MultiSelect` / `.Tags` (`creatable`) | `BadgeField` with free text | `string[]` — values not in `options` survive |
|
|
113
140
|
| `FormBuilder.RadioList` (`options`, each with optional `description`) | boxed radio list | `string` |
|
|
114
141
|
| `FormBuilder.CheckboxGroup` (`options`, each with optional `description`) | boxed checkbox list | `string[]` |
|
|
115
142
|
| `FormBuilder.RadioCards` (`options` with `description`) | `RadioCard` | `string` |
|
|
@@ -188,7 +215,10 @@ country code.
|
|
|
188
215
|
`FormBuilder.RadioList` (single-select, `string`) and `FormBuilder.CheckboxGroup` (multi-select,
|
|
189
216
|
`string[]`) render their `options` as a boxed, divided list — control on the left, primary
|
|
190
217
|
label, and an optional per-option `description` shown as a secondary label. The whole row is
|
|
191
|
-
clickable. Multi-select is also available as `.MultiSelect` / `.Tags` (a tag-chip picker).
|
|
218
|
+
clickable. Multi-select is also available as `.MultiSelect` / `.Tags` (a tag-chip picker). Add
|
|
219
|
+
`creatable` to either and the user can type a value that is not in `options` and commit it with
|
|
220
|
+
Enter or comma; pass `options={[]}` for a pure free-text list. (`createLabel` is a `BadgeField`
|
|
221
|
+
prop — it is not forwarded, so the create row keeps its default label inside a form.)
|
|
192
222
|
|
|
193
223
|
`FormBuilder.SwitchBox` (value `boolean`) is a switch wrapped in a `#f9f9f9` field box. It
|
|
194
224
|
renders like any other field — the `label` sits in the normal label column — and the box holds
|
|
@@ -201,6 +231,10 @@ presentation, so it lives there.
|
|
|
201
231
|
form** (via context), so it submits even when placed in a header / action bar that renders
|
|
202
232
|
_outside_ the `<form>` — no manual `form={id}` wiring.
|
|
203
233
|
|
|
234
|
+
It also takes `disabled`, for permission gating: someone with read access should still *see* a
|
|
235
|
+
record, so a Save they may not use is disabled rather than removed — a missing button looks broken,
|
|
236
|
+
a disabled one says "not yours to change". The server refuses the write either way.
|
|
237
|
+
|
|
204
238
|
## Moved to FormRenderer
|
|
205
239
|
|
|
206
240
|
These all used to live here. They are chrome, so they now live on
|
|
@@ -58,6 +58,7 @@ It renders in the form's header action pill (page) or the drawer header (drawer)
|
|
|
58
58
|
| `FormRenderer.Back` / `.Next` | Chevron step controls. The header's action bar prepends them for you. |
|
|
59
59
|
| `FormRenderer.Sidebar` / `.Tab` | The display-only [detail-tabs](#detail-tabs-sidebar) view. |
|
|
60
60
|
| `FormRenderer.Grid` / `.Row` | Read-only display cells inside a detail tab. |
|
|
61
|
+
| `FormRenderer.NotchAction` | A pill in the drawer's notch. You own the label, so it translates. |
|
|
61
62
|
|
|
62
63
|
### `FormRenderer.Section`
|
|
63
64
|
|
|
@@ -109,6 +110,10 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
109
110
|
| `actions` | `ReactNode` | The form's action bar — rendered in the header action pill (page) or drawer header (drawer). Put the Save here: `actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}`. A bare `FormBuilder.Submit` auto-targets this form. |
|
|
110
111
|
| `id` | `string` | `id` on the underlying `<form>`. Optional — FormRenderer generates and wires one otherwise. |
|
|
111
112
|
| `open` / `onOpenChange` / `title` / `badge` / `onOpenInNewTab` | — | Drawer control (when `display="drawer"`). `title` / `badge` are strings that override `header.title` / `header.label`. |
|
|
113
|
+
| `embedded` | `boolean` | Render without the rounded body card, for a host that already draws one. Defaults to `true` in a drawer. |
|
|
114
|
+
| `activeTab` / `onTabChange` | `string` / `(tab) => void` | Make the [detail-tabs](#detail-tabs-sidebar) rail controlled, so the tab can live in the URL (`?tab=audit`) and survive a reload. Omit both for the uncontrolled default. Inert in form mode. |
|
|
115
|
+
| `activeStep` / `onStepChange` | `number` / `(index) => void` | External control of a `FormRenderer.Stepper`'s step, for a wizard owned by something other than form validity. In controlled mode internal advancement is suppressed and a click is *reported*, not applied. Omit both and the stepper behaves exactly as before. |
|
|
116
|
+
| `drawer` | `{ side?; nested?; framed?; hideHeader?; bareBody?; description?; wrapperClassName?; className? }` | Drawer layout, forwarded to `FormDrawer` — see [Drawer layout](#drawer-layout). One object rather than eight flat props, since none of it means anything on a page. |
|
|
112
117
|
|
|
113
118
|
## Drawer
|
|
114
119
|
|
|
@@ -129,6 +134,60 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
129
134
|
</FormRenderer>
|
|
130
135
|
```
|
|
131
136
|
|
|
137
|
+
### Drawer layout
|
|
138
|
+
|
|
139
|
+
Everything about how the drawer is *shaped* goes in one `drawer` object, because none of it means
|
|
140
|
+
anything on a page:
|
|
141
|
+
|
|
142
|
+
| Key | Type | Default | What it does |
|
|
143
|
+
| ----------------- | ----------------------------- | --------------- | -------------------------------------------------------------------------------------------------- |
|
|
144
|
+
| `side` | `'inline-end' \| 'bottom'` | `'inline-end'` | Which edge it slides from. `'bottom'` is a sheet and drops the notch — there is no inline edge to hang it from. The default follows document direction. |
|
|
145
|
+
| `nested` | `boolean` | `false` | **Required** when this drawer opens inside another one, or the two roots fight over the overlay and the scroll lock. Throws without a parent Drawer. |
|
|
146
|
+
| `framed` | `boolean` | `true` | The dark tray frame and the panel's border / inset shadow. |
|
|
147
|
+
| `hideHeader` | `boolean` | `false` | Skip the header bar, for a child that draws its own. Also drops the body's top padding. |
|
|
148
|
+
| `bareBody` | `boolean` | `false` | Skip the padded scroll wrapper, for a child that already scrolls and offsets for its own header. |
|
|
149
|
+
| `description` | `string` | — | Screen-reader-only description. vaul warns when a drawer has none. |
|
|
150
|
+
| `wrapperClassName`| `string` | per `side` | Lands on the positioner — width, height, insets. Replaces the default sizing. |
|
|
151
|
+
| `className` | `string` | — | Lands on the tray. |
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// A bottom sheet, opened from inside another drawer.
|
|
155
|
+
<FormRenderer
|
|
156
|
+
display="drawer"
|
|
157
|
+
open={open}
|
|
158
|
+
onOpenChange={setOpen}
|
|
159
|
+
title="Quick add"
|
|
160
|
+
drawer={{ side: "bottom", nested: true, description: "Add a line item" }}
|
|
161
|
+
onSubmit={save}
|
|
162
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
163
|
+
>
|
|
164
|
+
{fields}
|
|
165
|
+
</FormRenderer>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `FormRenderer.NotchAction`
|
|
169
|
+
|
|
170
|
+
Buttons in the drawer's notch, authored by you. `onOpenInNewTab` still works, but it hardcodes an
|
|
171
|
+
English label and allows only one action — write `NotchAction` children instead and you own both.
|
|
172
|
+
They render nothing where you write them; the renderer lifts them into the notch.
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<FormRenderer display="drawer" open={open} onOpenChange={setOpen} title={t("invoice")}>
|
|
176
|
+
<FormRenderer.NotchAction onClick={openFullPage}>
|
|
177
|
+
{t("openInNewTab")}
|
|
178
|
+
<i className="ri-arrow-right-up-line text-[12px]" />
|
|
179
|
+
</FormRenderer.NotchAction>
|
|
180
|
+
<FormRenderer.NotchAction color="Blue" onClick={print}>
|
|
181
|
+
{t("print")}
|
|
182
|
+
</FormRenderer.NotchAction>
|
|
183
|
+
|
|
184
|
+
{fields}
|
|
185
|
+
</FormRenderer>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`color` matches `DrawerNotchPill` and defaults to `"Yellow"`. Ignored on a page-display form, and a
|
|
189
|
+
`side: "bottom"` sheet has no notch to put them in.
|
|
190
|
+
|
|
132
191
|
## Stepper
|
|
133
192
|
|
|
134
193
|
Drop a `FormRenderer.Stepper` in as the child. The Save lives in the header `actions` and
|
|
@@ -140,6 +199,10 @@ validates the current step, then advances (disabled on the last step). A step th
|
|
|
140
199
|
validation **stays checked** in the rail — even after you navigate back — while a live validation
|
|
141
200
|
error overrides it to red. You still pass just the Submit; the nav is wired for you:
|
|
142
201
|
|
|
202
|
+
The rail **stays put while the fields scroll**. It is navigation, so it pins below the header rather
|
|
203
|
+
than scrolling out of view with the form; a rail taller than the form body scrolls to its end first,
|
|
204
|
+
as any sticky element does.
|
|
205
|
+
|
|
143
206
|
```tsx
|
|
144
207
|
<FormRenderer
|
|
145
208
|
onSubmit={save}
|
|
@@ -266,7 +329,14 @@ outside the `<form>`, wire the Save button to the form via `id` / `form={id}`:
|
|
|
266
329
|
```
|
|
267
330
|
|
|
268
331
|
`FormDrawer` props: `open`, `onOpenChange`, `title`, `badge`, `variant`, `actions`,
|
|
269
|
-
`onOpenInNewTab`, `children`, `summary
|
|
332
|
+
`onOpenInNewTab`, `notchActions`, `children`, `summary`, plus the layout flags listed under
|
|
333
|
+
[Drawer layout](#drawer-layout) — `side`, `nested`, `framed`, `hideHeader`, `bareBody`,
|
|
334
|
+
`description`, `wrapperClassName`, `className`. It owns no form state.
|
|
335
|
+
|
|
336
|
+
`notchActions` is the notch's button slot. Going through `FormRenderer` you rarely set it directly —
|
|
337
|
+
write `FormRenderer.NotchAction` children and they are lifted into it for you. Using `FormDrawer` on
|
|
338
|
+
its own, pass the buttons here. When both `notchActions` and `onOpenInNewTab` are given, the former
|
|
339
|
+
wins; `onOpenInNewTab` remains only for callers happy with its built-in English label.
|
|
270
340
|
|
|
271
341
|
### The title
|
|
272
342
|
|
|
@@ -146,13 +146,14 @@ All standard `HTMLAttributes<HTMLDivElement>` (for example `id`, `aria-*`, `data
|
|
|
146
146
|
|---------|-----------|-----------|------------------------|
|
|
147
147
|
| `new` | `bg-blue-sparkle-alpha-50` | `text-blue-sparkle-200` | pill (`label`) → title |
|
|
148
148
|
| `edit` | `bg-orange-alpha-50` | `text-orange-200` | pill (`label`) → title |
|
|
149
|
-
| `detail` | `bg-white-alpha-
|
|
149
|
+
| `detail` | `bg-white-alpha-15` | `text-white-00` | title → pill (`label`) — positions swapped |
|
|
150
150
|
|
|
151
151
|
## Styling
|
|
152
152
|
|
|
153
153
|
- **Fixed dark container**: `rounded-[14px]`, `border-black-600`, `bg-black-1000`, `p-1.5`, with a double soft shadow. The surface is always dark regardless of theme.
|
|
154
154
|
- **Layout**: the root is `inline-flex`, so the chip hugs its content rather than stretching to fill its parent.
|
|
155
|
-
- **Typography**: 28px, weight 510
|
|
155
|
+
- **Typography**: `typography-display-medium-medium` (28px, weight 510) with the `cv05` stylistic set. Both `label` and `title` render uppercase.
|
|
156
|
+
- **Ordering**: `detail` swaps the two children in the DOM rather than reversing the row, so the pair stays correct under `dir="rtl"` and reads in order to assistive tech.
|
|
156
157
|
- **Emphasis pill**: the colored badge background and text color are driven entirely by `variant` (see the Variants table). For `detail`, the pill also moves to the right side.
|
|
157
158
|
|
|
158
159
|
## TypeScript Types
|
|
@@ -15,7 +15,7 @@ dependencies:
|
|
|
15
15
|
|
|
16
16
|
# InputField
|
|
17
17
|
|
|
18
|
-
> An enhanced input component that combines Input with integrated popover dropdowns, error
|
|
18
|
+
> An enhanced input component that combines Input with integrated popover dropdowns, error styling, icons, and trailing actions. Perfect for complex form fields requiring additional UI elements.
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
@@ -206,9 +206,9 @@ function PasswordField() {
|
|
|
206
206
|
| `icon` | `ReactNode` | - | Icon to display on the left side |
|
|
207
207
|
| `childrenSide` | `ReactNode` | - | Content to display on the right side |
|
|
208
208
|
| `popoverChildren` | `ReactNode` | - | Content for the dropdown popover |
|
|
209
|
-
| `errorMessage` | `string` | - |
|
|
209
|
+
| `errorMessage` | `string` | - | Marks the field invalid — any non-undefined value turns on the negative border |
|
|
210
210
|
| `onTable` | `boolean` | `false` | Optimized styling for table context |
|
|
211
|
-
| `toolTipSide` | `'top' \| 'right' \| 'bottom' \| 'left'` | - |
|
|
211
|
+
| `toolTipSide` | `'top' \| 'right' \| 'bottom' \| 'left'` | - | **Deprecated, ignored.** The error tooltip was removed; an invalid field is shown by its border alone |
|
|
212
212
|
| `theme` | `'dark' \| 'light' \| 'default'` | - | Theme variant |
|
|
213
213
|
| `className` | `string` | - | Additional CSS classes |
|
|
214
214
|
| `disabled` | `boolean` | `false` | Disables the input |
|
|
@@ -380,6 +380,12 @@ The layout has three stacked parts, and the order matters. Only the **scroller**
|
|
|
380
380
|
horizontally; the header actions above it and the end-action below it stay put, which is
|
|
381
381
|
what keeps `Add New` reachable on a wide table.
|
|
382
382
|
|
|
383
|
+
The section body is itself the horizontal scrollport, so a table wider than the card scrolls
|
|
384
|
+
**inside** it rather than being clipped or widening the page. One consequence worth knowing: a
|
|
385
|
+
scrollport is the containing block for `position: sticky`, so a `Table`'s sticky header inside a
|
|
386
|
+
section now resolves against a box that never scrolls vertically — i.e. it stops sticking. If you
|
|
387
|
+
need a sticky header, give the table its own vertical scroller.
|
|
388
|
+
|
|
383
389
|
```tsx
|
|
384
390
|
import { SectionBlock } from "@/components/SectionBlock";
|
|
385
391
|
import { Button } from "@/components/Button";
|
|
@@ -241,7 +241,7 @@ function Uncontrolled() {
|
|
|
241
241
|
|------|------|---------|-------------|
|
|
242
242
|
| `size` | `'S' \| 'M' \| 'L' \| 'XL'` | `'M'` | Size of the trigger |
|
|
243
243
|
| `variant` | `'PresentationStyle' \| 'SystemStyle'` | `'PresentationStyle'` | Visual variant |
|
|
244
|
-
| `errors` | `string` | - |
|
|
244
|
+
| `errors` | `string` | - | Marks the trigger invalid — any non-undefined value turns on the negative border |
|
|
245
245
|
| `theme` | `Themes` | - | Theme override |
|
|
246
246
|
| `icon` | `string` | - | Custom icon class |
|
|
247
247
|
| `className` | `string` | - | Additional CSS classes |
|
|
@@ -26,6 +26,25 @@ npx torch-glare@latest add Button
|
|
|
26
26
|
|
|
27
27
|
## Release notes
|
|
28
28
|
|
|
29
|
+
- **v2.5.5** — **breaking**: `DataViews.Filters.Summary` is removed with no shim; delete any
|
|
30
|
+
`<DataViews.Filters.Summary />` (render your own from `useDataViewsFilters()` if you want one).
|
|
31
|
+
`DrawerContent.notchSide` / `DrawerNotch.side` become logical `"start" | "end"` (was
|
|
32
|
+
`"left" | "right"`), and `TreeFolder`'s drag wiring is internal — `dragHandlers`,
|
|
33
|
+
`TreeFolderRowDragHandlers`, `UseTreeFolderDnDResult`, `scrollContainerRef` and
|
|
34
|
+
`getRowDragHandlers` are gone. New, non-breaking: `BadgeField` `creatable` / `createLabel`,
|
|
35
|
+
`FormRenderer` `embedded` / controlled tabs and steps / `drawer` layout options /
|
|
36
|
+
`FormRenderer.NotchAction`, `FormBuilder.Submit` `disabled`, and `DataViews.Empty`.
|
|
37
|
+
Also, every `FormBuilder.*` field takes a `hints` array, so one field can
|
|
38
|
+
carry several alerts. The validation error renders first, your hints follow. See
|
|
39
|
+
[FormBuilder](../components/form-builder.md#hints). Dropdown panels (`Select`,
|
|
40
|
+
`SearchableSelect`, `SearchableTree`, `Popover`, `DropdownMenu`, `ContextMenu`) now cap to the
|
|
41
|
+
space actually on screen and scroll their list in an inner viewport instead of being clipped —
|
|
42
|
+
note `SearchableTree`'s `maxBodyHeight` default drops 320 → 200, and menu submenus gained a
|
|
43
|
+
`maxHeight` (they were previously uncapped). `InputField`, `BadgeField` and `Select` no longer
|
|
44
|
+
pop an error tooltip — an invalid control is shown by its negative border alone, and
|
|
45
|
+
`toolTipSide` is deprecated and ignored. Plus fixes to `TabSwitch` dividers,
|
|
46
|
+
`FieldSection` hint placement, the
|
|
47
|
+
`FormRenderer` stepper grid, `SlideDatePicker` theming and `PopoverItem`'s disabled state.
|
|
29
48
|
- **v2.5.2** — **breaking**: `FormBuilder` now holds only the fields; the section cards, title
|
|
30
49
|
header and stepper moved to `FormRenderer`. See
|
|
31
50
|
[FormBuilder 2.5.2](./form-builder-2.5.2.md) for the rename table.
|
package/docs/reference/hooks.md
CHANGED
|
@@ -20,6 +20,7 @@ Custom React hooks that provide reusable functionality for common UI patterns. A
|
|
|
20
20
|
- **useClickOutside** - Detect clicks outside a referenced element
|
|
21
21
|
- **useResize** - Handle element resizing with RTL support
|
|
22
22
|
- **useTagSelection** - Manage tag selection state with keyboard navigation
|
|
23
|
+
- **useHtmlDir** - Track the document's text direction from `<html dir>`
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -1498,3 +1499,25 @@ All hooks support:
|
|
|
1498
1499
|
- Edge 90+
|
|
1499
1500
|
|
|
1500
1501
|
**IntersectionObserver** (useActiveTreeItem): Requires polyfill for older browsers.
|
|
1502
|
+
|
|
1503
|
+
---
|
|
1504
|
+
|
|
1505
|
+
## useHtmlDir
|
|
1506
|
+
|
|
1507
|
+
Tracks the document's text direction from `<html dir>`, re-reading it when it changes — a language
|
|
1508
|
+
switch, say. Returns `"ltr" | "rtl"`.
|
|
1509
|
+
|
|
1510
|
+
```tsx
|
|
1511
|
+
import { useHtmlDir } from "@/hooks/useHtmlDir";
|
|
1512
|
+
|
|
1513
|
+
const dir = useHtmlDir();
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
Most mirroring should be done in CSS with logical properties, which need no JS at all. Reach for
|
|
1517
|
+
this only where a library wants the direction as a **value**: several Radix primitives default to
|
|
1518
|
+
`"ltr"` when given no `dir` prop and no `DirectionProvider`, and vaul computes an inline transform
|
|
1519
|
+
from its `direction` prop, which a stylesheet cannot override mid-drag.
|
|
1520
|
+
|
|
1521
|
+
| Returns | Notes |
|
|
1522
|
+
| --- | --- |
|
|
1523
|
+
| `"ltr" \| "rtl"` | SSR-safe — returns `"ltr"` when there is no `document`. Watches the attribute with a `MutationObserver`, so a runtime language switch updates every consumer. |
|
|
@@ -702,3 +702,25 @@ These utilities depend on the following external libraries:
|
|
|
702
702
|
- [Tailwind CSS Documentation](https://tailwindcss.com)
|
|
703
703
|
- [date-fns Format Reference](https://date-fns.org/docs/format)
|
|
704
704
|
- [TypeScript Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html)
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## Scroller Utilities
|
|
709
|
+
|
|
710
|
+
### horizontalScrollerStyles
|
|
711
|
+
|
|
712
|
+
The design's 14px horizontal scroller as a class string: a thin track that thickens and turns blue
|
|
713
|
+
on hover. Shared by `TableScroller` and `SectionBlock`'s body so the two cannot drift.
|
|
714
|
+
|
|
715
|
+
```tsx
|
|
716
|
+
import { horizontalScrollerStyles } from "@/utils/scroller";
|
|
717
|
+
import { cn } from "@/utils/cn";
|
|
718
|
+
|
|
719
|
+
<div className={cn(horizontalScrollerStyles, "rounded-lg")}>
|
|
720
|
+
<table className="w-max">…</table>
|
|
721
|
+
</div>
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
It includes `overflow-x-auto overflow-y-hidden`, so the element it lands on becomes the scrollport —
|
|
725
|
+
you do not add an overflow class yourself.
|
|
726
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torch-glare",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A copy-in React component library (TypeScript + Radix UI + Tailwind CSS). Its CLI copies component source directly into your project — you own the code.",
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../../utils/cn";
|
|
4
|
-
import { formatPathLabel } from "../../../utils/dataViews/path";
|
|
5
|
-
import type { FilterValue } from "../../../utils/dataViews/types";
|
|
6
|
-
import { useDataViewsFilters, useDataViewsView } from "../context";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* `DataViews.Filters.Summary` — the active filters as removable chips. Useful when the controls
|
|
10
|
-
* themselves live in the panel and you still want the current query visible above the rows.
|
|
11
|
-
*
|
|
12
|
-
* Its labels come from the same descriptors `Filters` reads off its children — the **root** collects
|
|
13
|
-
* those as well, so this works wherever it is rendered, including as `Filters`' sibling. A path no
|
|
14
|
-
* control covers falls back to a label derived from the path.
|
|
15
|
-
*/
|
|
16
|
-
export function Summary({ className }: { className?: string }) {
|
|
17
|
-
const { filters, setFilters, filterFields } = useDataViewsFilters();
|
|
18
|
-
const { search, setSearch } = useDataViewsView();
|
|
19
|
-
const entries = Object.entries(filters);
|
|
20
|
-
|
|
21
|
-
if (entries.length === 0 && !search) return null;
|
|
22
|
-
|
|
23
|
-
const chip = cn(
|
|
24
|
-
"typography-body-small-regular flex items-center gap-1 rounded-[6px] px-2 py-[2px]",
|
|
25
|
-
"bg-background-presentation-action-selected text-content-presentation-global-primary",
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
const describe = (path: string, value: FilterValue) => {
|
|
29
|
-
const label = filterFields.find((f) => f.path === path)?.label ?? formatPathLabel(path);
|
|
30
|
-
if (Array.isArray(value)) return `${label}: ${value.join(", ")}`;
|
|
31
|
-
if (value.kind === "number") return `${label}: ${value.min ?? "…"}–${value.max ?? "…"}`;
|
|
32
|
-
return `${label}: ${value.from ?? "…"} → ${value.to ?? "…"}`;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div className={cn("flex flex-wrap items-center gap-2", className)}>
|
|
37
|
-
{/* The search term narrows the result set exactly as a filter does, so it belongs in the
|
|
38
|
-
summary of what is currently narrowing it — and it has to be clearable from here too. */}
|
|
39
|
-
{search && (
|
|
40
|
-
<span className={chip}>
|
|
41
|
-
{`Search: ${search}`}
|
|
42
|
-
<button type="button" aria-label="Clear search" onClick={() => setSearch("")}>
|
|
43
|
-
<i className="ri-close-line text-[12px]" />
|
|
44
|
-
</button>
|
|
45
|
-
</span>
|
|
46
|
-
)}
|
|
47
|
-
{entries.map(([path, value]) => (
|
|
48
|
-
<span key={path} className={chip}>
|
|
49
|
-
{describe(path, value)}
|
|
50
|
-
<button
|
|
51
|
-
type="button"
|
|
52
|
-
aria-label={`Remove ${path} filter`}
|
|
53
|
-
onClick={() => {
|
|
54
|
-
const copy = { ...filters };
|
|
55
|
-
delete copy[path];
|
|
56
|
-
setFilters(copy);
|
|
57
|
-
}}
|
|
58
|
-
>
|
|
59
|
-
<i className="ri-close-line text-[12px]" />
|
|
60
|
-
</button>
|
|
61
|
-
</span>
|
|
62
|
-
))}
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
}
|