@tristan2828/ui-foundation 4.61.1 → 4.63.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.
Files changed (59) hide show
  1. package/conventions/docs/cell-patterns.md +16 -11
  2. package/conventions/docs/design-language.md +132 -23
  3. package/dist/components/app/app-shell.js +28 -2
  4. package/dist/components/app/board.js +1 -1
  5. package/dist/components/app/data-table.js +1 -1
  6. package/dist/components/app/foundation-providers.d.ts +14 -1
  7. package/dist/components/app/foundation-providers.js +4 -3
  8. package/dist/components/app/markdown.d.ts +7 -2
  9. package/dist/components/app/markdown.js +15 -20
  10. package/dist/components/app/milkdown-editor.d.ts +1 -1
  11. package/dist/components/app/milkdown-editor.js +58 -17
  12. package/dist/components/app/module-card.js +1 -1
  13. package/dist/components/app/palette-provider.d.ts +14 -0
  14. package/dist/components/app/palette-provider.js +35 -0
  15. package/dist/components/app/prose-look.d.ts +7 -0
  16. package/dist/components/app/rich-text-editor.d.ts +8 -1
  17. package/dist/components/app/rich-text-editor.js +6 -0
  18. package/dist/components/app/rich-text-format-items.js +1 -1
  19. package/dist/components/app/rich-text-node-views.d.ts +9 -3
  20. package/dist/components/app/rich-text-node-views.js +12 -8
  21. package/dist/components/app/rich-text-table-grips.d.ts +31 -0
  22. package/dist/components/app/rich-text-table-grips.js +216 -0
  23. package/dist/components/app/rich-text-table.d.ts +19 -2
  24. package/dist/components/app/rich-text-table.js +232 -1
  25. package/dist/components/app/rich-text-toolbar.d.ts +4 -9
  26. package/dist/components/app/rich-text-toolbar.js +9 -11
  27. package/dist/components/app/rich-text-widgets.js +2 -2
  28. package/dist/components/app/widget-card.js +2 -2
  29. package/dist/components/ui/badge.d.ts +1 -1
  30. package/dist/components/ui/badge.js +5 -2
  31. package/dist/components/ui/button.js +10 -10
  32. package/dist/components/ui/card.js +3 -3
  33. package/dist/components/ui/checkbox.js +1 -1
  34. package/dist/components/ui/combobox.js +1 -1
  35. package/dist/components/ui/dialog.js +1 -1
  36. package/dist/components/ui/input-group.js +1 -1
  37. package/dist/components/ui/input.js +1 -1
  38. package/dist/components/ui/progress.d.ts +7 -0
  39. package/dist/components/ui/progress.js +19 -0
  40. package/dist/components/ui/select.js +1 -1
  41. package/dist/components/ui/sheet.js +1 -1
  42. package/dist/components/ui/slider.d.ts +3 -0
  43. package/dist/components/ui/slider.js +12 -0
  44. package/dist/components/ui/textarea.js +1 -1
  45. package/dist/hooks/use-palette.d.ts +14 -0
  46. package/dist/hooks/use-palette.js +7 -0
  47. package/dist/index.d.ts +3 -1
  48. package/dist/index.js +2 -0
  49. package/dist/lib/markdown-table.d.ts +28 -0
  50. package/dist/lib/markdown-table.js +106 -0
  51. package/dist/lib/palettes.d.ts +21 -0
  52. package/dist/lib/palettes.js +74 -0
  53. package/package.json +1 -1
  54. package/styles/index.css +152 -0
  55. package/styles/theme.css +321 -11
  56. package/vite/index.d.ts +7 -1
  57. package/vite/pwa.js +21 -7
  58. package/dist/components/app/rich-text-table-menu.d.ts +0 -20
  59. package/dist/components/app/rich-text-table-menu.js +0 -53
@@ -1,6 +1,7 @@
1
- import { type EditorState, type Transaction } from '@milkdown/kit/prose/state';
1
+ import { Plugin, type EditorState, type Transaction } from '@milkdown/kit/prose/state';
2
+ import { type HighlightColor } from '../../lib/markdown-highlight.js';
2
3
  type Dispatch = (tr: Transaction) => void;
3
- export type TableAction = 'rowAbove' | 'rowBelow' | 'columnLeft' | 'columnRight' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'deleteRow' | 'deleteColumn' | 'deleteTable';
4
+ export type TableAction = 'rowAbove' | 'rowBelow' | 'columnLeft' | 'columnRight' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'deleteRow' | 'deleteColumn' | 'deleteTable' | 'moveRowUp' | 'moveRowDown' | 'moveColumnLeft' | 'moveColumnRight' | 'sortAscending' | 'sortDescending' | 'headerColumn';
4
5
  export type ColumnAlignment = 'left' | 'center' | 'right';
5
6
  /** The caret's column's alignment, as the header cell has it (GFM's default is left). */
6
7
  export declare function columnAlignment(state: EditorState): ColumnAlignment;
@@ -8,6 +9,22 @@ export declare function columnAlignment(state: EditorState): ColumnAlignment;
8
9
  export declare function tableActionEnabled(state: EditorState, action: TableAction): boolean;
9
10
  /** Runs an action where the caret is. False when it can't apply there. */
10
11
  export declare function runTableAction(state: EditorState, dispatch: Dispatch, action: TableAction): boolean;
12
+ /** Whether the caret's table has a header column (its first cells bold). */
13
+ export declare function headerColumnOn(state: EditorState): boolean;
14
+ /** What a colour is put on: the caret's cell, its whole row, or its column's body cells. */
15
+ export type TableColorScope = 'cell' | 'row' | 'column';
16
+ /** Whether a colour can go on that scope where the caret is (never the header row's cells). */
17
+ export declare function tableColorEnabled(state: EditorState, scope: TableColorScope): boolean;
18
+ /** The cell colour where the caret is, or null (none, or the header row). */
19
+ export declare function cellColorAt(state: EditorState): HighlightColor | null;
20
+ /**
21
+ * Colours cells: each one's whole text put in that highlight (`null`
22
+ * takes the highlight off). An empty cell stays as it is: a highlight
23
+ * needs words.
24
+ */
25
+ export declare function colorTableCells(state: EditorState, dispatch: Dispatch, scope: TableColorScope, color: HighlightColor | null): boolean;
26
+ /** The plugin that draws cell colours and a header column (tableDecorations). */
27
+ export declare function tableLook(): Plugin;
11
28
  /**
12
29
  * The editor's table keys, before Milkdown's own (which go to the next
13
30
  * cell on Tab, and out of the table on any Enter). True when it handled
@@ -1,4 +1,32 @@
1
- import { TextSelection } from '@milkdown/kit/prose/state';
1
+ // What the rich-text editor can do to a table: rows and columns added,
2
+ // deleted and moved, a column's alignment, the rows sorted by a column,
3
+ // cells coloured, a header column, the whole table deleted, and its keys.
4
+ // Plain ProseMirror commands over prosemirror-tables and Milkdown's gfm
5
+ // schema, so each is one undo step.
6
+ //
7
+ // Sorting rewrites the rows' order: what the editor shows is what it
8
+ // saves, so a sort is an edit like any other (Ctrl+Z puts them back).
9
+ // Colours and the header column are stored as Markdown already writes
10
+ // them (src/lib/markdown-table.ts): a coloured cell is its whole text in
11
+ // one highlight, a header column is its first cells in bold. The table's
12
+ // decorations (tableLook) draw them.
13
+ //
14
+ // What GFM can't store, the editor doesn't offer: a table always keeps its
15
+ // header row and at least one row under it, and at least one column, so
16
+ // those deletes are off where they'd break that (the header row can't be
17
+ // deleted or have a row put above it). Alignment is per column, as the
18
+ // Markdown writes it (`:---:`).
19
+ //
20
+ // Keys (handleTableKey), on top of Milkdown's Tab/Shift+Tab between cells:
21
+ // Tab in the last cell adds a row; Enter goes to the cell below, and out
22
+ // of the table from its last row; Shift+Enter does nothing (a line break
23
+ // can't be stored inside a cell).
24
+ import { closeHistory } from '@milkdown/kit/prose/history';
25
+ import { Fragment } from '@milkdown/kit/prose/model';
26
+ import { Plugin, PluginKey, TextSelection } from '@milkdown/kit/prose/state';
27
+ import { Decoration, DecorationSet } from '@milkdown/kit/prose/view';
28
+ import { isHighlightColor } from '../../lib/markdown-highlight.js';
29
+ import { cellColor, compareCells, isHeaderColumn } from '../../lib/markdown-table.js';
2
30
  import { addColumnAfter, addColumnBefore, deleteColumn, deleteRow, deleteTable, isInTable, selectedRect, TableMap, } from '@milkdown/kit/prose/tables';
3
31
  const ALIGN = {
4
32
  alignLeft: 'left',
@@ -26,6 +54,19 @@ export function tableActionEnabled(state, action) {
26
54
  return top > 0 && bottom - top < map.height - 1;
27
55
  case 'deleteColumn':
28
56
  return right - left < map.width;
57
+ case 'moveRowUp':
58
+ return top > 1;
59
+ case 'moveRowDown':
60
+ return top > 0 && bottom < map.height;
61
+ case 'moveColumnLeft':
62
+ return left > 0;
63
+ case 'moveColumnRight':
64
+ return right < map.width;
65
+ case 'sortAscending':
66
+ case 'sortDescending':
67
+ return map.height > 2;
68
+ case 'headerColumn':
69
+ return map.height > 1;
29
70
  default:
30
71
  return true;
31
72
  }
@@ -52,10 +93,200 @@ export function runTableAction(state, dispatch, action) {
52
93
  return deleteColumn(state, dispatch);
53
94
  case 'deleteTable':
54
95
  return deleteTable(state, dispatch);
96
+ case 'moveRowUp':
97
+ case 'moveRowDown':
98
+ return moveRow(state, dispatch, action === 'moveRowUp' ? -1 : 1);
99
+ case 'moveColumnLeft':
100
+ case 'moveColumnRight':
101
+ return moveColumn(state, dispatch, action === 'moveColumnLeft' ? -1 : 1);
102
+ case 'sortAscending':
103
+ case 'sortDescending':
104
+ return sortRows(state, dispatch, action === 'sortAscending' ? 1 : -1);
105
+ case 'headerColumn':
106
+ return toggleHeaderColumn(state, dispatch);
55
107
  default:
56
108
  return false;
57
109
  }
58
110
  }
111
+ // The table rebuilt from its rows, in place of the old one, and the caret
112
+ // into the cell at `row`, `column` of the new one. Its own undo step,
113
+ // never merged with the edit before it (two sorts in a row undo one at a time).
114
+ function replaceRows(state, dispatch, rows, row, column) {
115
+ const { table, tableStart } = selectedRect(state);
116
+ const next = table.copy(Fragment.from(rows));
117
+ const tr = state.tr.replaceWith(tableStart - 1, tableStart - 1 + table.nodeSize, next);
118
+ const map = TableMap.get(next);
119
+ tr.setSelection(TextSelection.near(tr.doc.resolve(tableStart + map.map[row * map.width + column] + 1)));
120
+ dispatch(closeHistory(tr.scrollIntoView()));
121
+ return true;
122
+ }
123
+ const rowsOf = (table) => {
124
+ const rows = [];
125
+ table.forEach((row) => rows.push(row));
126
+ return rows;
127
+ };
128
+ // The caret's row swapped with the one above or below (never the header
129
+ // row: GFM's first row is always the header).
130
+ function moveRow(state, dispatch, by) {
131
+ const { table, top, left } = selectedRect(state);
132
+ const rows = rowsOf(table);
133
+ const to = top + by;
134
+ [rows[top], rows[to]] = [rows[to], rows[top]];
135
+ return replaceRows(state, dispatch, rows, to, left);
136
+ }
137
+ // The caret's column swapped with the one left or right of it, in every
138
+ // row; each cell keeps its alignment, so the column's moves with it.
139
+ function moveColumn(state, dispatch, by) {
140
+ const { table, top, left } = selectedRect(state);
141
+ const to = left + by;
142
+ const rows = rowsOf(table).map((row) => {
143
+ const cells = [];
144
+ row.forEach((cell) => cells.push(cell));
145
+ [cells[left], cells[to]] = [cells[to], cells[left]];
146
+ return row.copy(Fragment.from(cells));
147
+ });
148
+ return replaceRows(state, dispatch, rows, top, to);
149
+ }
150
+ // The body rows in the order of the caret's column (numbers by value,
151
+ // then words, empty cells last), the header row kept first. Rows that
152
+ // tie keep their order. The caret goes to the column's header.
153
+ function sortRows(state, dispatch, direction) {
154
+ const { table, left } = selectedRect(state);
155
+ const [header, ...body] = rowsOf(table);
156
+ const text = (row) => row.child(left).textContent;
157
+ const sorted = body
158
+ .map((row, index) => ({ row, index }))
159
+ .sort((a, b) => compareCells(text(a.row), text(b.row)) * direction || a.index - b.index)
160
+ .map(({ row }) => row);
161
+ return replaceRows(state, dispatch, [header, ...sorted], 0, left);
162
+ }
163
+ // A cell's text as runs, with their highlight and bold (markdown-table.ts).
164
+ function runsOf(cell) {
165
+ const runs = [];
166
+ cell.descendants((node) => {
167
+ if (!node.isText)
168
+ return true;
169
+ const highlight = node.marks.find((mark) => mark.type.name === 'highlight');
170
+ const color = highlight ? (isHighlightColor(highlight.attrs.color) ? highlight.attrs.color : 'amber') : null;
171
+ runs.push({ text: node.text ?? '', color, bold: node.marks.some((mark) => mark.type.name === 'strong') });
172
+ return false;
173
+ });
174
+ return runs;
175
+ }
176
+ // Each body row's first cell, and where it starts in the document.
177
+ function firstCells(table, tableStart) {
178
+ const cells = [];
179
+ table.forEach((row, offset, index) => {
180
+ if (index > 0 && row.firstChild)
181
+ cells.push({ cell: row.firstChild, pos: tableStart + offset + 1 });
182
+ });
183
+ return cells;
184
+ }
185
+ /** Whether the caret's table has a header column (its first cells bold). */
186
+ export function headerColumnOn(state) {
187
+ if (!isInTable(state))
188
+ return false;
189
+ const { table, tableStart } = selectedRect(state);
190
+ return isHeaderColumn(firstCells(table, tableStart).map(({ cell }) => runsOf(cell)));
191
+ }
192
+ // The header column on (each body row's first cell bolded) or off (unbolded).
193
+ function toggleHeaderColumn(state, dispatch) {
194
+ const { table, tableStart } = selectedRect(state);
195
+ const strong = state.schema.marks.strong;
196
+ const on = headerColumnOn(state);
197
+ const tr = state.tr;
198
+ for (const { cell, pos } of firstCells(table, tableStart)) {
199
+ const from = pos + 1;
200
+ const to = pos + cell.nodeSize - 1;
201
+ if (on)
202
+ tr.removeMark(from, to, strong);
203
+ else if (cell.textContent.trim())
204
+ tr.addMark(from, to, strong.create());
205
+ }
206
+ if (tr.docChanged)
207
+ dispatch(closeHistory(tr));
208
+ return true;
209
+ }
210
+ /** Whether a colour can go on that scope where the caret is (never the header row's cells). */
211
+ export function tableColorEnabled(state, scope) {
212
+ if (!isInTable(state))
213
+ return false;
214
+ const { map, top } = selectedRect(state);
215
+ return scope === 'column' ? map.height > 1 : top > 0;
216
+ }
217
+ /** The cell colour where the caret is, or null (none, or the header row). */
218
+ export function cellColorAt(state) {
219
+ if (!isInTable(state))
220
+ return null;
221
+ const { table, map, left, top } = selectedRect(state);
222
+ const cell = table.nodeAt(map.map[top * map.width + left]);
223
+ return cell ? cellColor(runsOf(cell)) : null;
224
+ }
225
+ /**
226
+ * Colours cells: each one's whole text put in that highlight (`null`
227
+ * takes the highlight off). An empty cell stays as it is: a highlight
228
+ * needs words.
229
+ */
230
+ export function colorTableCells(state, dispatch, scope, color) {
231
+ if (!tableColorEnabled(state, scope))
232
+ return false;
233
+ const { map, table, tableStart, top, bottom, left, right } = selectedRect(state);
234
+ const highlight = state.schema.marks.highlight;
235
+ const rows = scope === 'column' ? [1, map.height] : [top, bottom];
236
+ const columns = scope === 'row' ? [0, map.width] : [left, right];
237
+ const tr = state.tr;
238
+ for (let row = rows[0]; row < rows[1]; row++) {
239
+ for (let column = columns[0]; column < columns[1]; column++) {
240
+ const offset = map.map[row * map.width + column];
241
+ const cell = table.nodeAt(offset);
242
+ const from = tableStart + offset + 1;
243
+ const to = tableStart + offset + cell.nodeSize - 1;
244
+ tr.removeMark(from, to, highlight);
245
+ if (color && cell.textContent.trim())
246
+ tr.addMark(from, to, highlight.create({ color }));
247
+ }
248
+ }
249
+ if (tr.docChanged)
250
+ dispatch(closeHistory(tr));
251
+ return true;
252
+ }
253
+ // The decorations that draw what the Markdown keeps: a coloured cell's
254
+ // wash (`data-color`) and a header column's first cells (`data-row-header`),
255
+ // styled in styles/index.css ("Tables in written text").
256
+ function tableDecorations(doc) {
257
+ const decorations = [];
258
+ doc.descendants((node, pos) => {
259
+ if (node.type.name !== 'table')
260
+ return node.isBlock && !node.isTextblock;
261
+ const tableStart = pos + 1;
262
+ node.forEach((row, rowOffset) => {
263
+ row.forEach((cell, cellOffset) => {
264
+ const color = cellColor(runsOf(cell));
265
+ const at = tableStart + rowOffset + 1 + cellOffset;
266
+ if (color)
267
+ decorations.push(Decoration.node(at, at + cell.nodeSize, { 'data-color': color }));
268
+ });
269
+ });
270
+ const firsts = firstCells(node, tableStart);
271
+ if (isHeaderColumn(firsts.map(({ cell }) => runsOf(cell))))
272
+ for (const { cell, pos: at } of firsts)
273
+ decorations.push(Decoration.node(at, at + cell.nodeSize, { 'data-row-header': '' }));
274
+ return false;
275
+ });
276
+ return DecorationSet.create(doc, decorations);
277
+ }
278
+ const tableLookKey = new PluginKey('tableLook');
279
+ /** The plugin that draws cell colours and a header column (tableDecorations). */
280
+ export function tableLook() {
281
+ return new Plugin({
282
+ key: tableLookKey,
283
+ state: {
284
+ init: (_config, state) => tableDecorations(state.doc),
285
+ apply: (tr, previous) => (tr.docChanged ? tableDecorations(tr.doc) : previous),
286
+ },
287
+ props: { decorations: (state) => tableLookKey.getState(state) },
288
+ });
289
+ }
59
290
  // A row of empty cells, each aligned as its column is, above or below the
60
291
  // caret's rows (or at the end: `'end'`), and the caret into it in the
61
292
  // same column (the first, at the end).
@@ -2,7 +2,6 @@ import { type Ref } from 'react';
2
2
  import type { EditorState } from '@milkdown/kit/prose/state';
3
3
  import { type FormatItem } from './rich-text-format-items.js';
4
4
  import { type FormatId } from './rich-text-formats.js';
5
- import type { TableAction } from './rich-text-table.js';
6
5
  declare const SETS: {
7
6
  all: FormatItem[][];
8
7
  tools: FormatItem[][];
@@ -16,18 +15,16 @@ export type FormatButtonsProps = {
16
15
  readOnly: boolean;
17
16
  /** A button pressed: run the format on the editor and give it focus back. */
18
17
  onFormat: (id: FormatId) => void;
19
- /** A Table menu item picked (only the fixed toolbar has the menu). */
20
- onTable?: (action: TableAction) => void;
21
18
  /** The Image button pressed: pick images to upload. No button without it. */
22
19
  onImage?: () => void;
23
20
  /** The Full screen button pressed. No button without it. */
24
21
  onFullScreen?: () => void;
25
22
  /** Whether the editor is full screen now (the button's state). */
26
23
  fullScreen?: boolean;
27
- /** The editable element, for the Table menu to give focus back to. */
24
+ /** The editable element, for the Highlight menu to give focus back to. */
28
25
  text?: () => HTMLElement | null;
29
26
  };
30
- export declare function FormatButtons({ formats, state, readOnly, onFormat, onTable, onImage, onFullScreen, fullScreen, text }: FormatButtonsProps): import("react").JSX.Element;
27
+ export declare function FormatButtons({ formats, state, readOnly, onFormat, onImage, onFullScreen, fullScreen, text }: FormatButtonsProps): import("react").JSX.Element;
31
28
  export interface RichTextToolbarProps {
32
29
  /** The editor's state now, to show which formats are on. Null until it's made. */
33
30
  state: EditorState | null;
@@ -36,18 +33,16 @@ export interface RichTextToolbarProps {
36
33
  controls?: string;
37
34
  /** A button pressed: run the format on the editor and give it focus back. */
38
35
  onFormat: (id: FormatId) => void;
39
- /** A Table menu item picked: run it on the editor and give it focus back. */
40
- onTable: (action: TableAction) => void;
41
36
  /** The Image button pressed (only when the editor uploads images). */
42
37
  onImage?: () => void;
43
38
  /** The Full screen button pressed. No button without it. */
44
39
  onFullScreen?: () => void;
45
40
  /** Whether the editor is full screen now. */
46
41
  fullScreen?: boolean;
47
- /** The editable element, for the Table menu to give focus back to. */
42
+ /** The editable element, for the Highlight menu to give focus back to. */
48
43
  text: () => HTMLElement | null;
49
44
  /** The toolbar's element, for Alt+F10 to reach it from the text. */
50
45
  ref?: Ref<HTMLDivElement>;
51
46
  }
52
- export declare function RichTextToolbar({ state, readOnly, controls, onFormat, onTable, onImage, onFullScreen, fullScreen, text, ref }: RichTextToolbarProps): import("react").JSX.Element;
47
+ export declare function RichTextToolbar({ state, readOnly, controls, onFormat, onImage, onFullScreen, fullScreen, text, ref }: RichTextToolbarProps): import("react").JSX.Element;
53
48
  export {};
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // The rich-text editor's fixed toolbar: one row of pressed/not-pressed
3
3
  // buttons above the text, each a format and its keyboard shortcut, a
4
- // Table menu (rich-text-table-menu.tsx), and, when the editor uploads
5
- // images, an Image button (a plain button: it opens the file picker).
4
+ // Table button that inserts one (a table's own grips edit it:
5
+ // rich-text-table-grips.tsx), and, when the editor uploads images, an
6
+ // Image button (a plain button: it opens the file picker).
6
7
  //
7
8
  // - One tab stop (ARIA's toolbar pattern): Tab reaches the last button
8
9
  // used, the arrow keys, Home and End move along the row.
@@ -24,7 +25,6 @@ import { FORMAT_GROUPS, FORMAT_ITEMS } from './rich-text-format-items.js';
24
25
  import { formatOn } from './rich-text-formats.js';
25
26
  import { ariaKeys, isApple, shownKeys } from './rich-text-shortcuts.js';
26
27
  import { RichTextHighlightMenu } from './rich-text-highlight-menu.js';
27
- import { RichTextTableMenu } from './rich-text-table-menu.js';
28
28
  // Which buttons a toolbar has: every format (the fixed one), or the ones
29
29
  // that apply to selected words (the floating one).
30
30
  const SELECTION = ['bold', 'italic', 'strike', 'code', 'highlight', 'link'];
@@ -51,7 +51,7 @@ function useCompact() {
51
51
  // A toolbar's row of buttons: one tab stop, the last one used (ARIA's
52
52
  // toolbar pattern); the arrow keys, Home and End move along the row. The
53
53
  // toolbar's element (`role="toolbar"`, its name) is the caller's.
54
- export function FormatButtons({ formats, state, readOnly, onFormat, onTable, onImage, onFullScreen, fullScreen = false, text = () => null }) {
54
+ export function FormatButtons({ formats, state, readOnly, onFormat, onImage, onFullScreen, fullScreen = false, text = () => null }) {
55
55
  const apple = isApple();
56
56
  const compact = useCompact();
57
57
  const groups = SETS[formats].map((group) => group.filter((item) => compact || !item.touch)).filter((group) => group.length > 0);
@@ -83,10 +83,6 @@ export function FormatButtons({ formats, state, readOnly, onFormat, onTable, onI
83
83
  };
84
84
  return (_jsxs(TooltipProvider, { delay: 400, children: [groups.map((group, groupIndex) => (_jsxs(Fragment, { children: [groupIndex > 0 && _jsx(Separator, { orientation: "vertical", className: "mx-1 h-5 self-center" }), group.map((item) => {
85
85
  const index = items.indexOf(item);
86
- if (item.id === 'table')
87
- return (_jsx(RichTextTableMenu, { ref: (element) => {
88
- buttons.current[index] = element;
89
- }, state: state, readOnly: readOnly, tabIndex: index === current ? 0 : -1, onFocus: () => setCurrent(index), onKeyDown: onKeyDown, onInsert: () => onFormat('table'), onAction: (action) => onTable?.(action), text: text }, item.id));
90
86
  if (item.id === 'highlight')
91
87
  return (_jsx(RichTextHighlightMenu, { ref: (element) => {
92
88
  buttons.current[index] = element;
@@ -102,7 +98,9 @@ export function FormatButtons({ formats, state, readOnly, onFormat, onTable, onI
102
98
  if (item.action)
103
99
  return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { render: _jsx(Button, { ref: (element) => {
104
100
  buttons.current[index] = element;
105
- }, variant: "ghost", size: "icon-sm", "aria-label": item.label, disabled: readOnly, tabIndex: index === current ? 0 : -1, onMouseDown: (event) => event.preventDefault(), onFocus: () => setCurrent(index), onKeyDown: onKeyDown, onClick: () => onFormat(item.id) }), children: _jsx(Icon, {}) }), _jsx(TooltipContent, { children: tip })] }, item.id));
101
+ }, variant: "ghost", size: "icon-sm", "aria-label": item.label,
102
+ // A table goes in only outside one.
103
+ disabled: readOnly || (item.id === 'table' && state !== null && formatOn(state, 'table')), tabIndex: index === current ? 0 : -1, onMouseDown: (event) => event.preventDefault(), onFocus: () => setCurrent(index), onKeyDown: onKeyDown, onClick: () => onFormat(item.id) }), children: _jsx(Icon, {}) }), _jsx(TooltipContent, { children: tip })] }, item.id));
106
104
  return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { render: _jsx(Toggle, { ref: (element) => {
107
105
  buttons.current[index] = element;
108
106
  }, size: "icon-sm", "aria-label": item.label, "aria-keyshortcuts": item.keys ? ariaKeys(item.keys, apple) : undefined, pressed: state ? formatOn(state, item.id) : false, disabled: readOnly, tabIndex: index === current ? 0 : -1, className: "aria-pressed:bg-muted aria-pressed:text-foreground",
@@ -114,6 +112,6 @@ export function FormatButtons({ formats, state, readOnly, onFormat, onTable, onI
114
112
  buttons.current[fullScreenIndex] = element;
115
113
  }, size: "icon-sm", "aria-label": "Full screen", pressed: fullScreen, tabIndex: fullScreenIndex === current ? 0 : -1, className: "aria-pressed:bg-muted aria-pressed:text-foreground", onMouseDown: (event) => event.preventDefault(), onFocus: () => setCurrent(fullScreenIndex), onKeyDown: onKeyDown, onPressedChange: onFullScreen }), children: fullScreen ? _jsx(Minimize2Icon, {}) : _jsx(Maximize2Icon, {}) }), _jsx(TooltipContent, { children: fullScreen ? 'Leave full screen · Esc' : 'Full screen' })] })] }))] }));
116
114
  }
117
- export function RichTextToolbar({ state, readOnly, controls, onFormat, onTable, onImage, onFullScreen, fullScreen, text, ref }) {
118
- return (_jsx("div", { ref: ref, role: "toolbar", "aria-label": "Formatting", "aria-controls": controls, "data-slot": "rich-text-toolbar", className: "flex flex-wrap items-center gap-0.5 border-b border-input px-1 py-1", children: _jsx(FormatButtons, { formats: "all", state: state, readOnly: readOnly, onFormat: onFormat, onTable: onTable, onImage: onImage, onFullScreen: onFullScreen, fullScreen: fullScreen, text: text }) }));
115
+ export function RichTextToolbar({ state, readOnly, controls, onFormat, onImage, onFullScreen, fullScreen, text, ref }) {
116
+ return (_jsx("div", { ref: ref, role: "toolbar", "aria-label": "Formatting", "aria-controls": controls, "data-slot": "rich-text-toolbar", className: "flex flex-wrap items-center gap-0.5 border-b border-input px-1 py-1", children: _jsx(FormatButtons, { formats: "all", state: state, readOnly: readOnly, onFormat: onFormat, onImage: onImage, onFullScreen: onFullScreen, fullScreen: fullScreen, text: text }) }));
119
117
  }
@@ -8,8 +8,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  // the tab order.
9
9
  // - A table's bars add a column at its right and a row at its end, shown
10
10
  // while the pointer is over the table or the caret is in it. Mouse
11
- // shortcuts as well: Tab in the last cell adds a row, and the toolbar's
12
- // Table menu does both.
11
+ // shortcuts as well: Tab in the last cell adds a row, and a cell's menu
12
+ // (its grips, or Shift+F10: rich-text-table-grips.tsx) does both.
13
13
  //
14
14
  // Each gives focus back to the text when it's done.
15
15
  import { PlusIcon } from 'lucide-react';
@@ -90,7 +90,7 @@ export function WidgetCard({ id, title, icon: Icon, headingLevel = 2, link, isLo
90
90
  return;
91
91
  toggle();
92
92
  };
93
- return (_jsx("section", { id: id, "aria-labelledby": headingId, "data-slot": "widget-card", "data-state": state, "data-collapsed": collapsed || undefined, className: cn('min-w-0 self-start', className), children: _jsxs(Card, { className: "density-compact:[--card-spacing:--spacing(3)]", children: [_jsxs(CardHeader, { onClick: onHeaderClick, className: cn('flex items-center justify-between gap-3', inAccordion && 'cursor-pointer select-none'), children: [_jsxs(Heading, { id: headingId, className: "flex min-w-0 items-center gap-2 type-section-title text-foreground", children: [Icon && _jsx(Icon, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate", children: title })] }), (link || foldable || summaryShown) && (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [summaryShown && (_jsx("span", { id: summaryId, "data-slot": "widget-card-summary", className: "mr-2 flex items-center", children: _jsx(WidgetCardSummary, { state: state, summary: summary, emptySummary: emptySummary }) })), link?.href !== undefined ? (_jsxs("a", { href: link.href, target: "_blank", rel: "noreferrer", "aria-describedby": headingId, className: cn(HEADER_LINK_CLASS, 'inline-flex items-center gap-1'), children: [link.label, _jsx(ExternalLinkIcon, { "aria-hidden": "true", className: "size-3 shrink-0 text-muted-foreground" }), _jsx("span", { className: "sr-only", children: " (opens in a new tab)" })] })) : (link && (_jsx(Link, { to: link.to, "aria-describedby": headingId, className: HEADER_LINK_CLASS, children: link.label }))), foldable && (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-labelledby": headingId, "aria-describedby": summaryShown ? summaryId : undefined, "aria-expanded": !collapsed, "aria-controls": contentId, onClick: toggle,
93
+ return (_jsx("section", { id: id, "aria-labelledby": headingId, "data-slot": "widget-card", "data-state": state, "data-collapsed": collapsed || undefined, className: cn('min-w-0 self-start', className), children: _jsxs(Card, { className: "glow-edge density-compact:[--card-spacing:--spacing(3)]", children: [_jsxs(CardHeader, { onClick: onHeaderClick, className: cn('flex items-center justify-between gap-3', inAccordion && 'cursor-pointer select-none'), children: [_jsxs(Heading, { id: headingId, className: "flex min-w-0 items-center gap-2 type-section-title text-foreground", children: [Icon && _jsx(Icon, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "truncate", children: title })] }), (link || foldable || summaryShown) && (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [summaryShown && (_jsx("span", { id: summaryId, "data-slot": "widget-card-summary", className: "mr-2 flex items-center", children: _jsx(WidgetCardSummary, { state: state, summary: summary, emptySummary: emptySummary }) })), link?.href !== undefined ? (_jsxs("a", { href: link.href, target: "_blank", rel: "noreferrer", "aria-describedby": headingId, className: cn(HEADER_LINK_CLASS, 'inline-flex items-center gap-1'), children: [link.label, _jsx(ExternalLinkIcon, { "aria-hidden": "true", className: "size-3 shrink-0 text-muted-foreground" }), _jsx("span", { className: "sr-only", children: " (opens in a new tab)" })] })) : (link && (_jsx(Link, { to: link.to, "aria-describedby": headingId, className: HEADER_LINK_CLASS, children: link.label }))), foldable && (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-labelledby": headingId, "aria-describedby": summaryShown ? summaryId : undefined, "aria-expanded": !collapsed, "aria-controls": contentId, onClick: toggle,
94
94
  // The button's own open look (a filled background) would
95
95
  // read as pressed: the chevron says it.
96
96
  className: "-my-1 -mr-1.5 aria-expanded:not-hover:bg-transparent", children: _jsx(ChevronDownIcon, { "aria-hidden": "true", className: cn('transition-transform', collapsed && '-rotate-90') }) }))] }))] }), _jsx(CardContent, { id: contentId, hidden: collapsed, "aria-busy": state === 'loading' || undefined, children: error ? (_jsx(ErrorState, { error: error, onRetry: onRetry, size: "compact" })) : state === 'loading' ? (_jsx(WidgetListSkeleton, { rows: skeletonRows })) : state === 'empty' ? (_jsx("p", { "data-state": "empty", className: "py-2 type-body text-muted-foreground", children: emptyMessage })) : (children) })] }) }));
@@ -127,7 +127,7 @@ const ROW_CLASS = 'flex min-h-9 items-center gap-3 py-2 density-compact:min-h-8
127
127
  // Varied widths, so the skeleton reads as a list of titles, not a grid.
128
128
  const SKELETON_WIDTHS = ['w-3/5', 'w-2/5', 'w-1/2', 'w-2/3', 'w-1/3', 'w-3/5'];
129
129
  function WidgetListSkeleton({ rows }) {
130
- return (_jsxs("div", { "data-state": "loading", className: "flex flex-col divide-y divide-border", children: [_jsx("span", { className: "sr-only", children: "Loading" }), Array.from({ length: rows }, (_, row) => (_jsxs("div", { className: ROW_CLASS, children: [_jsx(Skeleton, { className: cn('h-4', SKELETON_WIDTHS[row % SKELETON_WIDTHS.length]) }), _jsx(Skeleton, { className: "ml-auto h-4 w-12" })] }, row)))] }));
130
+ return (_jsxs("div", { "data-state": "loading", className: "flex flex-col divide-y divide-border", children: [_jsx("span", { className: "sr-only", children: "Loading" }), Array.from({ length: rows }, (_, row) => (_jsxs("div", { className: cn(ROW_CLASS, 'type-body'), children: [_jsx("span", { "aria-hidden": "true", className: "-mr-3 w-0 overflow-hidden", children: "\u00A0" }), _jsx(Skeleton, { className: cn('h-4', SKELETON_WIDTHS[row % SKELETON_WIDTHS.length]) }), _jsx(Skeleton, { className: "ml-auto h-4 w-12" })] }, row)))] }));
131
131
  }
132
132
  /** The rows of a WidgetCard: a list with a divider between rows. */
133
133
  export function WidgetList({ children, className }) {
@@ -1,7 +1,7 @@
1
1
  import { useRender } from "@base-ui/react/use-render";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  declare const badgeVariants: (props?: ({
4
- variant?: "link" | "success" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "warning" | "info" | "outline-success" | "outline-warning" | "outline-info" | "outline-destructive" | "tinted-success" | "tinted-warning" | "tinted-info" | "tinted-destructive" | null | undefined;
4
+ variant?: "link" | "success" | "default" | "outline" | "secondary" | "ghost" | "destructive" | "solid" | "warning" | "info" | "outline-success" | "outline-warning" | "outline-info" | "outline-destructive" | "tinted-success" | "tinted-warning" | "tinted-info" | "tinted-destructive" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  declare function Badge({ className, variant, render, ...props }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
7
7
  export { Badge, badgeVariants };
@@ -5,7 +5,10 @@ import { cn } from "cn";
5
5
  const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
6
6
  variants: {
7
7
  variant: {
8
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
8
+ // The default is a soft tint of the accent (the shared style
9
+ // guide's badge); `solid` is the old filled look.
10
+ default: "bg-primary/12 text-foreground dark:bg-primary/20 dark:text-link [a]:hover:bg-primary/20",
11
+ solid: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
9
12
  secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
10
13
  destructive: "bg-destructive text-destructive-foreground focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/90",
11
14
  success: "bg-success text-success-foreground focus-visible:ring-success/20 dark:focus-visible:ring-success/40 [a]:hover:bg-success/90",
@@ -30,7 +33,7 @@ const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-cent
30
33
  "tinted-info": "bg-info/15 text-info-text dark:bg-info/20",
31
34
  "tinted-destructive": "bg-destructive/15 text-destructive-text dark:bg-destructive/20",
32
35
  ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
33
- link: "text-primary underline-offset-4 hover:underline",
36
+ link: "text-link underline-offset-4 hover:underline",
34
37
  },
35
38
  },
36
39
  defaultVariants: {
@@ -2,25 +2,25 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Button as ButtonPrimitive } from "@base-ui/react/button";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { cn } from "cn";
5
- const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
5
+ const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-semibold whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform] outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 motion-safe:active:not-aria-[haspopup]:scale-96 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
6
6
  variants: {
7
7
  variant: {
8
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
8
+ default: "bg-primary text-primary-foreground hover:bg-primary/80 palette:hover:shadow-(--primary-glow) palette:focus-visible:shadow-(--primary-glow) palette:active:shadow-(--primary-glow)",
9
9
  outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
10
10
  secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
11
11
  ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
12
12
  destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
13
- link: "text-primary underline-offset-4 hover:underline",
13
+ link: "text-link underline-offset-4 hover:underline",
14
14
  },
15
15
  size: {
16
- default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
16
+ default: "h-9 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
17
17
  xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
18
- sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-(length:--control-text-sm) in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
19
- lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
20
- icon: "size-8",
18
+ sm: "h-8 gap-1 rounded-[min(var(--radius-md),12px)] px-3 text-(length:--control-text-sm) in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
19
+ lg: "h-10 gap-1.5 px-5 has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4",
20
+ icon: "size-9",
21
21
  "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
22
- "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
23
- "icon-lg": "size-9",
22
+ "icon-sm": "size-8 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
23
+ "icon-lg": "size-10",
24
24
  },
25
25
  },
26
26
  defaultVariants: {
@@ -29,6 +29,6 @@ const buttonVariants = cva("group/button inline-flex shrink-0 items-center justi
29
29
  },
30
30
  });
31
31
  function Button({ className, variant = "default", size = "default", ...props }) {
32
- return (_jsx(ButtonPrimitive, { "data-slot": "button", className: cn(buttonVariants({ variant, size, className })), ...props }));
32
+ return (_jsx(ButtonPrimitive, { "data-slot": "button", "data-variant": variant, className: cn(buttonVariants({ variant, size, className })), ...props }));
33
33
  }
34
34
  export { Button, buttonVariants };
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "cn";
4
4
  function Card({ className, size = "default", ...props }) {
5
- return (_jsx("div", { "data-slot": "card", "data-size": size, className: cn("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", className), ...props }));
5
+ return (_jsx("div", { "data-slot": "card", "data-size": size, className: cn("group/card flex flex-col gap-(--card-gap) overflow-hidden rounded-lg bg-card py-(--card-spacing) text-sm text-card-foreground shadow-(--card-shadow) ring-1 ring-border [--card-gap:--spacing(3.5)] [--card-spacing:--spacing(4)] has-[>[data-slot=card-header]:first-child]:pt-0 has-[>[data-slot=card-header]:last-child]:pb-0 data-[size=sm]:[--card-gap:--spacing(3)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg", className), ...props }));
6
6
  }
7
7
  function CardHeader({ className, ...props }) {
8
- return (_jsx("div", { "data-slot": "card-header", className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)", className), ...props }));
8
+ return (_jsx("div", { "data-slot": "card-header", className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing) first:border-b first:border-border first:bg-card-header first:py-[calc(var(--card-spacing)*0.75)] last:border-b-0", className), ...props }));
9
9
  }
10
10
  function CardTitle({ className, ...props }) {
11
11
  return (_jsx("div", { "data-slot": "card-title", className: cn("font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className), ...props }));
@@ -20,6 +20,6 @@ function CardContent({ className, ...props }) {
20
20
  return (_jsx("div", { "data-slot": "card-content", className: cn("px-(--card-spacing)", className), ...props }));
21
21
  }
22
22
  function CardFooter({ className, ...props }) {
23
- return (_jsx("div", { "data-slot": "card-footer", className: cn("flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)", className), ...props }));
23
+ return (_jsx("div", { "data-slot": "card-footer", className: cn("flex items-center border-t bg-muted/50 p-(--card-spacing)", className), ...props }));
24
24
  }
25
25
  export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
@@ -3,6 +3,6 @@ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
3
3
  import { cn } from "cn";
4
4
  import { CheckIcon } from "lucide-react";
5
5
  function Checkbox({ className, ...props }) {
6
- return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
6
+ return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer relative flex size-4.5 shrink-0 items-center justify-center rounded-[5px] border border-input transition-[color,background-color,border-color,box-shadow,transform] outline-none motion-safe:active:scale-85 group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "grid place-content-center text-current transition-none [&>svg]:size-3.5", children: _jsx(CheckIcon, {}) }) }));
7
7
  }
8
8
  export { Checkbox };
@@ -44,7 +44,7 @@ function ComboboxSeparator({ className, ...props }) {
44
44
  return (_jsx(ComboboxPrimitive.Separator, { "data-slot": "combobox-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
45
45
  }
46
46
  function ComboboxChips({ className, ...props }) {
47
- return (_jsx(ComboboxPrimitive.Chips, { "data-slot": "combobox-chips", className: cn("flex min-h-8 flex-wrap items-center gap-1 rounded-lg border border-input bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40", className), ...props }));
47
+ return (_jsx(ComboboxPrimitive.Chips, { "data-slot": "combobox-chips", className: cn("flex min-h-9 flex-wrap items-center gap-1 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40", className), ...props }));
48
48
  }
49
49
  function ComboboxChip({ className, children, showRemove = true, ...props }) {
50
50
  return (_jsxs(ComboboxPrimitive.Chip, { "data-slot": "combobox-chip", className: cn("flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0", className), ...props, children: [children, showRemove && (_jsx(ComboboxPrimitive.ChipRemove
@@ -17,7 +17,7 @@ function DialogClose({ ...props }) {
17
17
  return _jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
18
18
  }
19
19
  function DialogOverlay({ className, ...props }) {
20
- return (_jsx(DialogPrimitive.Backdrop, { "data-slot": "dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className), ...props }));
20
+ return (_jsx(DialogPrimitive.Backdrop, { "data-slot": "dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-black/20 duration-100 supports-backdrop-filter:backdrop-blur-sm dark:bg-black/40 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className), ...props }));
21
21
  }
22
22
  function DialogContent({ className, children, showCloseButton = true, ...props }) {
23
23
  return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Popup, { "data-slot": "dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props, children: [children, showCloseButton && (_jsxs(DialogPrimitive.Close, { "data-slot": "dialog-close", render: _jsx(Button, { variant: "ghost", className: "absolute top-2 right-2", size: "icon-sm" }), children: [_jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: "Close" })] }))] })] }));
@@ -6,7 +6,7 @@ import { Button } from "./button.js";
6
6
  import { Input } from "./input.js";
7
7
  import { Textarea } from "./textarea.js";
8
8
  function InputGroup({ className, ...props }) {
9
- return (_jsx("div", { "data-slot": "input-group", role: "group", className: cn("group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", className), ...props }));
9
+ return (_jsx("div", { "data-slot": "input-group", role: "group", className: cn("group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", className), ...props }));
10
10
  }
11
11
  const inputGroupAddonVariants = cva("flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", {
12
12
  variants: {
@@ -3,6 +3,6 @@ import * as React from "react";
3
3
  import { Input as InputPrimitive } from "@base-ui/react/input";
4
4
  import { cn } from "cn";
5
5
  function Input({ className, type, ...props }) {
6
- return (_jsx(InputPrimitive, { type: type, "data-slot": "input", className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
6
+ return (_jsx(InputPrimitive, { type: type, "data-slot": "input", className: cn("h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
7
7
  }
8
8
  export { Input };