@gradeui/ui 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ui/combobox.md +46 -0
- package/components/ui/data-view.md +59 -0
- package/components/ui/map.md +6 -0
- package/components/ui/property-list.md +43 -0
- package/dist/contracts.js +6 -6
- package/dist/contracts.js.map +1 -1
- package/dist/contracts.mjs +6 -6
- package/dist/contracts.mjs.map +1 -1
- package/dist/index.d.mts +323 -26
- package/dist/index.d.ts +323 -26
- package/dist/index.js +94 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -94
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -1
- package/styles/globals.css +179 -92
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Combobox
|
|
3
|
+
import: "@gradeui/ui"
|
|
4
|
+
props:
|
|
5
|
+
- options: { value, label, icon?, keywords?, disabled? }[] — the selectable pool
|
|
6
|
+
- value?: string | null — controlled selection (wire onValueChange)
|
|
7
|
+
- defaultValue?: string | null — uncontrolled initial selection
|
|
8
|
+
- onValueChange?: (next: string | null) => void — fired with the next value, or null when cleared
|
|
9
|
+
- placeholder?: string — trigger text when nothing is selected
|
|
10
|
+
- searchPlaceholder?: string — search-input placeholder
|
|
11
|
+
- emptyMessage?: string — shown when search returns no rows
|
|
12
|
+
- searchable?: boolean — show the search input (default true)
|
|
13
|
+
- clearable?: boolean — add a Clear row so the value can return to unset
|
|
14
|
+
- triggerVariant?: "default" | "inline" — default = form-control surface (like Select); inline = chrome-free token trigger
|
|
15
|
+
- renderValue?: (option) => ReactNode — render the selected value yourself (e.g. a Badge); falls back to icon + label
|
|
16
|
+
- hideChevron?: boolean — drop the trailing chevron (inline token look)
|
|
17
|
+
- disabled?: boolean — lock to a read-only display of the current value
|
|
18
|
+
- align?: "start" | "center" | "end" — popover alignment
|
|
19
|
+
when_to_use: Single-pick searchable picker — the single-select sibling of MultiSelect and the Linear "selectable badge" pattern (status / priority / assignee). Use triggerVariant="inline" with renderValue returning a Badge to make a value read as a clickable token that opens a command menu. For multiple selection use MultiSelect; for a small fixed list with no search use Select; for free-form command palettes use Command directly. Pass disabled (driven by a permission check) to show the value without letting the user edit it.
|
|
20
|
+
composes_with: [Popover, Command, Badge, Avatar, PropertyList, Table, Field]
|
|
21
|
+
aliases: [combobox, single select, searchable select, picker, status picker, priority picker, assignee picker, command select, autocomplete, dropdown select, selectable badge, inline select, token select, linear combobox]
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
<Combobox
|
|
26
|
+
options={[
|
|
27
|
+
{ value: "low", label: "Low" },
|
|
28
|
+
{ value: "medium", label: "Medium" },
|
|
29
|
+
{ value: "high", label: "High" },
|
|
30
|
+
]}
|
|
31
|
+
defaultValue="low"
|
|
32
|
+
placeholder="Set priority"
|
|
33
|
+
/>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```jsx
|
|
37
|
+
// Linear-style: the value IS the trigger.
|
|
38
|
+
<Combobox
|
|
39
|
+
triggerVariant="inline"
|
|
40
|
+
hideChevron
|
|
41
|
+
options={priorityOptions}
|
|
42
|
+
value={priority}
|
|
43
|
+
onValueChange={setPriority}
|
|
44
|
+
renderValue={(opt) => <Badge variant="warning-soft">{opt.label}</Badge>}
|
|
45
|
+
/>
|
|
46
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: DataView
|
|
3
|
+
import: "@gradeui/ui"
|
|
4
|
+
props:
|
|
5
|
+
- data: T[] — the rows
|
|
6
|
+
- columns: { key, header, type?, options?, cell?, role?, sortable?, pinned?, width?, align?, hideable?, defaultHidden? }[] — the schema; one list drives table, cards, and grid
|
|
7
|
+
- getRowId?: (row, i) => string — defaults to row.id
|
|
8
|
+
- view? / defaultView? / onViewChange?: "table" | "cards" | "grid" — controlled or uncontrolled view
|
|
9
|
+
- views?: ("table" | "cards" | "grid")[] — allowed views; one entry = single view, no toggle
|
|
10
|
+
- activeId? / defaultActiveId? / onActiveChange?: string | null — the selected row; click emits it
|
|
11
|
+
- sorting? / defaultSorting? / onSortingChange? — TanStack SortingState
|
|
12
|
+
- columnVisibility? / defaultColumnVisibility? / onColumnVisibilityChange? — which fields show
|
|
13
|
+
- stickyHeader?: boolean — freeze the header row on scroll
|
|
14
|
+
- toolbar?: boolean — render the built-in columns menu + view toggle above the view
|
|
15
|
+
- renderCard?: (row, { active }) => ReactNode — override card / grid tiles
|
|
16
|
+
- emptyMessage?: ReactNode
|
|
17
|
+
when_to_use: One dataset, drawn as a table, a list of cards, or a grid — without re-typing the TanStack boilerplate (sortable headers, flexRender, selection, view switch) on every page. Hand it data + a columns schema; columns declare a `type` (badge/tags/number/currency/percent/date/boolean/url/text) that DataView renders, with a `cell` override for bespoke cells (avatars, relations). The view toggle can live anywhere — `useDataView()` holds the state so a `<DataViewToggle>` or `<DataViewColumns>` in a page header drives a `<DataView>` lower down. Mark a column `pinned="left"` (with a `width`) for a fixed column and `stickyHeader` to freeze the header. For a single record's fields use PropertyList; for the raw table primitive use Table.
|
|
18
|
+
composes_with: [Table, Card, Badge, Avatar, ToggleGroup, DropdownMenu, PropertyList, Combobox]
|
|
19
|
+
aliases: [data view, data table, datatable, data grid, dataview, table view, card view, grid view, list view, gallery, records list, master list, tanstack table, sortable table, column visibility, pinned column, frozen column, sticky header, view switcher]
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
```jsx
|
|
23
|
+
const dv = useDataView({ defaultView: "table", defaultActiveId: rows[0].id });
|
|
24
|
+
|
|
25
|
+
// The toggle / columns menu can live anywhere — they just read dv.
|
|
26
|
+
<Row justify="between">
|
|
27
|
+
<h1>Alerts</h1>
|
|
28
|
+
<Row gap="sm">
|
|
29
|
+
<DataView.Columns columns={columns} visibility={dv.columnVisibility} onVisibilityChange={dv.setColumnVisibility} />
|
|
30
|
+
<DataView.Toggle value={dv.view} onChange={dv.setView} views={dv.views} />
|
|
31
|
+
</Row>
|
|
32
|
+
</Row>
|
|
33
|
+
|
|
34
|
+
<DataView
|
|
35
|
+
data={rows}
|
|
36
|
+
columns={columns}
|
|
37
|
+
view={dv.view}
|
|
38
|
+
activeId={dv.activeId}
|
|
39
|
+
onActiveChange={dv.setActiveId}
|
|
40
|
+
sorting={dv.sorting}
|
|
41
|
+
onSortingChange={dv.setSorting}
|
|
42
|
+
columnVisibility={dv.columnVisibility}
|
|
43
|
+
onColumnVisibilityChange={dv.setColumnVisibility}
|
|
44
|
+
stickyHeader
|
|
45
|
+
/>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```jsx
|
|
49
|
+
// Self-contained: built-in toolbar, single column pinned, table only.
|
|
50
|
+
<DataView
|
|
51
|
+
data={rows}
|
|
52
|
+
toolbar
|
|
53
|
+
columns={[
|
|
54
|
+
{ key: "name", header: "Name", role: "title", pinned: "left", width: 220 },
|
|
55
|
+
{ key: "status", header: "Status", type: "badge", options: statusOptions, sortable: true },
|
|
56
|
+
{ key: "arr", header: "ARR", type: "currency", align: "end", sortable: true },
|
|
57
|
+
]}
|
|
58
|
+
/>
|
|
59
|
+
```
|
package/components/ui/map.md
CHANGED
|
@@ -90,3 +90,9 @@ ANTI-PATTERNS — don't do these:
|
|
|
90
90
|
- DO NOT render >500 markers without clustering. The component warns in dev. For larger datasets, drop to `.instance` and use the provider's clustering layer.
|
|
91
91
|
|
|
92
92
|
Markers are DOM — children inherit `--gds-*` tokens. Drop a `<Card>`, `<Badge>`, `<Avatar>`, or anything else inside `<MapMarker>` and it themes correctly.
|
|
93
|
+
|
|
94
|
+
Stacking inside a marker follows normal DOM order on every provider — you do NOT need `z-index` hacks to layer content (e.g. a count label sitting on top of an inline pin-shield `<svg>`). The DS neutralizes Leaflet's vendor rule that sets `z-index: 200` on map `<svg>` elements (via `[data-gds-part="map-marker-content"] svg { z-index: auto }` in `globals.css`); without it, an inline SVG would paint above later siblings on Leaflet (the default provider) but not on Mapbox/MapLibre/Google, making the same marker markup look provider-dependent. If you nest an inline SVG behind text in a marker, just rely on source order.
|
|
95
|
+
|
|
96
|
+
For a floating text label over busy tiles, add the `gds-map-label` class — it applies a mode-aware halo (`--gds-map-label-halo`: white stroke on light tiles, near-black on dark) so the label never washes out. Don't hard-code a white `-webkit-text-stroke`.
|
|
97
|
+
|
|
98
|
+
Note: `<Map>` carries no border-radius or border of its own — it's an unopinionated primitive (the container clips with `overflow: hidden`). Round or frame it from the call site with `className` (e.g. `rounded-xl border`).
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: PropertyList
|
|
3
|
+
import: "@gradeui/ui"
|
|
4
|
+
props:
|
|
5
|
+
- layout?: "row" | "stack" — row (default): label column beside value; stack: label above value for narrow panels
|
|
6
|
+
- density?: "compact" | "default" | "relaxed" — row rhythm
|
|
7
|
+
- align?: "start" | "center" — default vertical alignment of label vs value; use start when values wrap (tag groups, multi-line)
|
|
8
|
+
- divider?: boolean — hairline rule between rows
|
|
9
|
+
- labelWidth?: string — override the label column width (any CSS length); sets --gds-property-list-label-width
|
|
10
|
+
- children: PropertyList.Row[]
|
|
11
|
+
when_to_use: Read-only display of the properties of a SINGLE item — detail panels, inspectors, "about this" cards, order/record summaries. It is a Table row transposed (schema vertical, one record). The value side is a polymorphic slot, so the same renderers that fill a Table cell (text, Badge, tag group, Avatar stack, date, link) drop straight into a row. For an EDITABLE field (label + control) use Field instead; a panel that flips between read and edit swaps a PropertyList for a stack of Fields.
|
|
12
|
+
composes_with: [Badge, Avatar, Table, Field, Separator, Card]
|
|
13
|
+
aliases: [property list, properties, property panel, description list, definition list, detail list, key value, key-value, data list, field list, attributes, metadata list, record summary, detail panel, inspector fields, spec list]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
```jsx
|
|
17
|
+
<PropertyList>
|
|
18
|
+
<PropertyList.Row label="Status" icon={<Activity />}>
|
|
19
|
+
<Badge variant="warning-soft">Low</Badge>
|
|
20
|
+
</PropertyList.Row>
|
|
21
|
+
<PropertyList.Row label="Published">2026-06-18</PropertyList.Row>
|
|
22
|
+
<PropertyList.Row label="Owner">
|
|
23
|
+
<Avatar className="h-5 w-5"><AvatarFallback>EO</AvatarFallback></Avatar>
|
|
24
|
+
</PropertyList.Row>
|
|
25
|
+
</PropertyList>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```jsx
|
|
29
|
+
<PropertyList density="compact" divider align="start">
|
|
30
|
+
<PropertyList.Row label="Topics">
|
|
31
|
+
<Row gap="xs" wrap>
|
|
32
|
+
<Badge variant="secondary">Pricing</Badge>
|
|
33
|
+
<Badge variant="secondary">Onboarding</Badge>
|
|
34
|
+
</Row>
|
|
35
|
+
</PropertyList.Row>
|
|
36
|
+
<PropertyList.Row label="Business profiles">
|
|
37
|
+
<Row gap="xs" wrap>
|
|
38
|
+
<Badge variant="outline">Acme</Badge>
|
|
39
|
+
<Badge variant="outline">Kite</Badge>
|
|
40
|
+
</Row>
|
|
41
|
+
</PropertyList.Row>
|
|
42
|
+
</PropertyList>
|
|
43
|
+
```
|