@stll/ui 0.0.1-placeholder.0 → 0.3.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/README.md +84 -0
- package/dist/components/accordion.d.ts +8 -0
- package/dist/components/accordion.js +38 -0
- package/dist/components/alert-dialog.d.ts +32 -0
- package/dist/components/alert-dialog.js +75 -0
- package/dist/components/avatar.d.ts +7 -0
- package/dist/components/avatar.js +22 -0
- package/dist/components/bidi-text.d.ts +24 -0
- package/dist/components/bidi-text.js +38 -0
- package/dist/components/brand-icons.d.ts +7 -0
- package/dist/components/brand-icons.js +18 -0
- package/dist/components/breadcrumb.d.ts +12 -0
- package/dist/components/breadcrumb.js +61 -0
- package/dist/components/button-variants.d.ts +17 -0
- package/dist/components/button-variants.js +43 -0
- package/dist/components/button.d.ts +20 -0
- package/dist/components/button.js +53 -0
- package/dist/components/checkbox.d.ts +5 -0
- package/dist/components/checkbox.js +44 -0
- package/dist/components/color-picker.d.ts +47 -0
- package/dist/components/color-picker.js +249 -0
- package/dist/components/combobox.d.ts +40 -0
- package/dist/components/combobox.js +208 -0
- package/dist/components/command.d.ts +34 -0
- package/dist/components/command.js +100 -0
- package/dist/components/date-picker-popover.d.ts +29 -0
- package/dist/components/date-picker-popover.js +548 -0
- package/dist/components/date-picker-popover.logic.d.ts +14 -0
- package/dist/components/date-picker-popover.logic.js +26 -0
- package/dist/components/destructive-action-confirmation.d.ts +21 -0
- package/dist/components/destructive-action-confirmation.js +60 -0
- package/dist/components/destructive-confirm-dialog.d.ts +17 -0
- package/dist/components/destructive-confirm-dialog.js +49 -0
- package/dist/components/dialog.d.ts +37 -0
- package/dist/components/dialog.js +92 -0
- package/dist/components/directional-icon.d.ts +30 -0
- package/dist/components/directional-icon.js +12 -0
- package/dist/components/field.d.ts +11 -0
- package/dist/components/field.js +34 -0
- package/dist/components/form.d.ts +5 -0
- package/dist/components/form.js +12 -0
- package/dist/components/frame.d.ts +10 -0
- package/dist/components/frame.js +35 -0
- package/dist/components/hex-color-picker.d.ts +11 -0
- package/dist/components/hex-color-picker.js +366 -0
- package/dist/components/input-group.d.ts +15 -0
- package/dist/components/input-group.js +53 -0
- package/dist/components/input-otp.d.ts +13 -0
- package/dist/components/input-otp.js +39 -0
- package/dist/components/input.d.ts +12 -0
- package/dist/components/input.js +46 -0
- package/dist/components/label.d.ts +5 -0
- package/dist/components/label.js +18 -0
- package/dist/components/menu.d.ts +44 -0
- package/dist/components/menu.js +152 -0
- package/dist/components/outline-rail.d.ts +43 -0
- package/dist/components/outline-rail.js +416 -0
- package/dist/components/pagination.d.ts +17 -0
- package/dist/components/pagination.js +78 -0
- package/dist/components/popover.d.ts +34 -0
- package/dist/components/popover.js +67 -0
- package/dist/components/preview-card.d.ts +12 -0
- package/dist/components/preview-card.js +25 -0
- package/dist/components/preview-pane.d.ts +23 -0
- package/dist/components/preview-pane.js +33 -0
- package/dist/components/scroll-area.d.ts +19 -0
- package/dist/components/scroll-area.js +38 -0
- package/dist/components/scroll-to-top.d.ts +12 -0
- package/dist/components/scroll-to-top.js +43 -0
- package/dist/components/secret-input.d.ts +9 -0
- package/dist/components/secret-input.js +38 -0
- package/dist/components/segmented-icon-toggle.d.ts +22 -0
- package/dist/components/segmented-icon-toggle.js +30 -0
- package/dist/components/select.d.ts +25 -0
- package/dist/components/select.js +196 -0
- package/dist/components/separator.d.ts +16 -0
- package/dist/components/separator.js +26 -0
- package/dist/components/sheet.d.ts +29 -0
- package/dist/components/sheet.js +87 -0
- package/dist/components/skeleton.d.ts +4 -0
- package/dist/components/skeleton.js +10 -0
- package/dist/components/stella-mark.d.ts +5 -0
- package/dist/components/stella-mark.js +12 -0
- package/dist/components/stella-wordmark.d.ts +5 -0
- package/dist/components/stella-wordmark.js +21 -0
- package/dist/components/table.d.ts +20 -0
- package/dist/components/table.js +80 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +46 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +31 -0
- package/dist/components/toast.d.ts +60 -0
- package/dist/components/toast.js +261 -0
- package/dist/components/tooltip-trigger-helper.d.ts +11 -0
- package/dist/components/tooltip-trigger-helper.js +14 -0
- package/dist/components/tooltip.d.ts +15 -0
- package/dist/components/tooltip.js +32 -0
- package/dist/data-table/index.d.ts +2 -0
- package/dist/data-table/index.js +2 -0
- package/dist/data-table/schema.d.ts +70 -0
- package/dist/data-table/schema.js +40 -0
- package/dist/hooks/use-contained-handler.d.ts +55 -0
- package/dist/hooks/use-contained-handler.js +54 -0
- package/dist/hooks/use-content-dir.d.ts +48 -0
- package/dist/hooks/use-content-dir.js +45 -0
- package/dist/hooks/use-mobile.d.ts +4 -0
- package/dist/hooks/use-mobile.js +16 -0
- package/dist/hooks/use-viewport-width.d.ts +5 -0
- package/dist/hooks/use-viewport-width.js +14 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +65 -0
- package/dist/inspector/chrome.d.ts +72 -0
- package/dist/inspector/chrome.js +153 -0
- package/dist/inspector/dock.d.ts +48 -0
- package/dist/inspector/dock.js +36 -0
- package/dist/inspector/index.d.ts +6 -0
- package/dist/inspector/index.js +6 -0
- package/dist/inspector/layout-tokens.d.ts +28 -0
- package/dist/inspector/layout-tokens.js +28 -0
- package/dist/inspector/pane-width.d.ts +64 -0
- package/dist/inspector/pane-width.js +53 -0
- package/dist/inspector/use-pane-width.d.ts +99 -0
- package/dist/inspector/use-pane-width.js +149 -0
- package/dist/kanban/card-properties.d.ts +27 -0
- package/dist/kanban/card-properties.js +19 -0
- package/dist/kanban/card-shell.d.ts +32 -0
- package/dist/kanban/card-shell.js +46 -0
- package/dist/kanban/column-header.d.ts +23 -0
- package/dist/kanban/column-header.js +24 -0
- package/dist/kanban/grouping.d.ts +84 -0
- package/dist/kanban/grouping.js +76 -0
- package/dist/kanban/index.d.ts +5 -0
- package/dist/kanban/index.js +5 -0
- package/dist/lib/button-disposition.d.ts +63 -0
- package/dist/lib/button-disposition.js +52 -0
- package/dist/lib/option-color.d.ts +28 -0
- package/dist/lib/option-color.js +59 -0
- package/dist/lib/overlay-layer.d.ts +22 -0
- package/dist/lib/overlay-layer.js +21 -0
- package/dist/lib/tab-order.d.ts +15 -0
- package/dist/lib/tab-order.js +15 -0
- package/dist/lib/tooltip-content.d.ts +13 -0
- package/dist/lib/tooltip-content.js +12 -0
- package/dist/lib/utils.d.ts +20 -0
- package/dist/lib/utils.js +41 -0
- package/dist/lib/week.d.ts +25 -0
- package/dist/lib/week.js +40 -0
- package/dist/styles/theme.css +1209 -0
- package/package.json +518 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/kanban/column-header.d.ts
|
|
3
|
+
type KanbanColumnHeaderProps = {
|
|
4
|
+
/** Colour swatch, or the control that changes it. */
|
|
5
|
+
swatch?: ReactNode;
|
|
6
|
+
/** The column name, or the editor that has taken its place. */
|
|
7
|
+
title: ReactNode;
|
|
8
|
+
/** Short text after the name, such as the card count the caller formatted. */
|
|
9
|
+
meta?: ReactNode;
|
|
10
|
+
/** Column calculation, rendered after the count. */
|
|
11
|
+
calculation?: ReactNode;
|
|
12
|
+
/** Drag affordance, revealed on hover over the column. */
|
|
13
|
+
dragHandle?: ReactNode;
|
|
14
|
+
/** Column menu. */
|
|
15
|
+
actions?: ReactNode;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The column header row: one rhythm for the swatch, the name, the count, the
|
|
19
|
+
* calculation, and the column's controls, so every board's header lines up.
|
|
20
|
+
*/
|
|
21
|
+
declare const KanbanColumnHeader: ({ swatch, title, meta, calculation, dragHandle, actions }: KanbanColumnHeaderProps) => import("react").JSX.Element;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { KanbanColumnHeader, KanbanColumnHeaderProps };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/kanban/column-header.tsx
|
|
3
|
+
/**
|
|
4
|
+
* The column header row: one rhythm for the swatch, the name, the count, the
|
|
5
|
+
* calculation, and the column's controls, so every board's header lines up.
|
|
6
|
+
*/
|
|
7
|
+
const KanbanColumnHeader = ({ swatch, title, meta, calculation, dragHandle, actions }) => /* @__PURE__ */ jsxs("div", {
|
|
8
|
+
className: "flex items-center gap-2 px-3 py-2",
|
|
9
|
+
children: [
|
|
10
|
+
swatch,
|
|
11
|
+
/* @__PURE__ */ jsxs("span", {
|
|
12
|
+
className: "flex min-w-0 flex-1 items-center gap-1.5 truncate",
|
|
13
|
+
children: [title, meta !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
14
|
+
className: "text-muted-foreground text-xs",
|
|
15
|
+
children: meta
|
|
16
|
+
})]
|
|
17
|
+
}),
|
|
18
|
+
calculation,
|
|
19
|
+
dragHandle,
|
|
20
|
+
actions
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { KanbanColumnHeader };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { OptionColor } from "../lib/option-color.js";
|
|
2
|
+
//#region src/kanban/grouping.d.ts
|
|
3
|
+
/** One column, before the uncategorized bucket is appended. */
|
|
4
|
+
type KanbanGroupOption = {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
color?: string | undefined;
|
|
8
|
+
colorBg?: string | undefined;
|
|
9
|
+
optionColor?: OptionColor | undefined;
|
|
10
|
+
};
|
|
11
|
+
/** A column, including the uncategorized bucket (`value: null`). */
|
|
12
|
+
type KanbanGroup = {
|
|
13
|
+
value: string | null;
|
|
14
|
+
label: string;
|
|
15
|
+
color?: string | undefined;
|
|
16
|
+
colorBg?: string | undefined;
|
|
17
|
+
optionColor?: OptionColor | undefined;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A column source that is not a schema property, addressed by a reserved id.
|
|
21
|
+
*
|
|
22
|
+
* The board's columns are the caller's `options`, in the caller's order, so a
|
|
23
|
+
* built-in grouping is a data declaration rather than a branch this module has
|
|
24
|
+
* to know about.
|
|
25
|
+
*/
|
|
26
|
+
type KanbanBuiltInGroup<TRow> = {
|
|
27
|
+
/** Reserved group id, distinct from any property id. */
|
|
28
|
+
id: string;
|
|
29
|
+
/** Ordered columns, without the uncategorized bucket. */
|
|
30
|
+
options: readonly KanbanGroupOption[];
|
|
31
|
+
/**
|
|
32
|
+
* Narrows the board to the rows this grouping can place. Omit when every row
|
|
33
|
+
* belongs on the board.
|
|
34
|
+
*/
|
|
35
|
+
selectRows?: ((rows: readonly TRow[]) => TRow[]) | undefined;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Everything the grouping needs to know about a board.
|
|
39
|
+
*
|
|
40
|
+
* `getPropertyOptions` returns `null` for a property that cannot carry columns,
|
|
41
|
+
* which keeps the decision — and the property model it depends on — with the
|
|
42
|
+
* caller.
|
|
43
|
+
*/
|
|
44
|
+
type KanbanSchema<TRow, TProperty> = {
|
|
45
|
+
builtInGroups: readonly KanbanBuiltInGroup<TRow>[];
|
|
46
|
+
properties: readonly TProperty[];
|
|
47
|
+
getPropertyId: (property: TProperty) => string;
|
|
48
|
+
getPropertyOptions: (property: TProperty) => readonly KanbanGroupOption[] | null;
|
|
49
|
+
};
|
|
50
|
+
type KanbanGrouping<TRow, TProperty> = {
|
|
51
|
+
type: "none";
|
|
52
|
+
} | {
|
|
53
|
+
type: "built-in";
|
|
54
|
+
propertyId: string;
|
|
55
|
+
group: KanbanBuiltInGroup<TRow>;
|
|
56
|
+
} | {
|
|
57
|
+
type: "property";
|
|
58
|
+
propertyId: string;
|
|
59
|
+
property: TProperty;
|
|
60
|
+
options: readonly KanbanGroupOption[];
|
|
61
|
+
};
|
|
62
|
+
type ResolveKanbanGroupingParams<TRow, TProperty> = {
|
|
63
|
+
/** The group-by id the view carries; empty means no grouping. */
|
|
64
|
+
groupBy: string;
|
|
65
|
+
schema: KanbanSchema<TRow, TProperty>;
|
|
66
|
+
};
|
|
67
|
+
/** Resolve a stored group-by id against a schema. */
|
|
68
|
+
declare const resolveKanbanGrouping: <TRow, TProperty>({ groupBy, schema }: ResolveKanbanGroupingParams<TRow, TProperty>) => KanbanGrouping<TRow, TProperty>;
|
|
69
|
+
declare const getKanbanGroupingPropertyId: <TRow, TProperty>(grouping: KanbanGrouping<TRow, TProperty>) => string | null;
|
|
70
|
+
/**
|
|
71
|
+
* A grouping with no columns is not a board. A built-in grouping declares its
|
|
72
|
+
* columns up front, so an empty declaration is the signal that the board cannot
|
|
73
|
+
* be drawn; a property grouping always draws, even when the property has no
|
|
74
|
+
* options yet, because rows still land in the uncategorized bucket.
|
|
75
|
+
*/
|
|
76
|
+
declare const isKanbanGroupingRenderable: <TRow, TProperty>(grouping: KanbanGrouping<TRow, TProperty>) => boolean;
|
|
77
|
+
/** The rows a grouping can place on the board, in their incoming order. */
|
|
78
|
+
declare const selectKanbanRows: <TRow, TProperty>(rows: readonly TRow[], grouping: KanbanGrouping<TRow, TProperty>) => TRow[];
|
|
79
|
+
/** The static column options for a grouping, excluding uncategorized. */
|
|
80
|
+
declare const resolveKanbanGroupOptions: <TRow, TProperty>(grouping: KanbanGrouping<TRow, TProperty>) => readonly KanbanGroupOption[];
|
|
81
|
+
/** Append the uncategorized bucket (null value) after the options. */
|
|
82
|
+
declare const getKanbanGroups: (options: readonly KanbanGroupOption[], uncategorizedLabel: string) => KanbanGroup[];
|
|
83
|
+
//#endregion
|
|
84
|
+
export { KanbanBuiltInGroup, KanbanGroup, KanbanGroupOption, KanbanGrouping, KanbanSchema, ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/kanban/grouping.ts
|
|
2
|
+
/** Resolve a stored group-by id against a schema. */
|
|
3
|
+
const resolveKanbanGrouping = ({ groupBy, schema }) => {
|
|
4
|
+
if (groupBy === "") return { type: "none" };
|
|
5
|
+
const builtIn = schema.builtInGroups.find((group) => group.id === groupBy);
|
|
6
|
+
if (builtIn !== void 0) return {
|
|
7
|
+
type: "built-in",
|
|
8
|
+
propertyId: groupBy,
|
|
9
|
+
group: builtIn
|
|
10
|
+
};
|
|
11
|
+
const property = schema.properties.find((candidate) => schema.getPropertyId(candidate) === groupBy);
|
|
12
|
+
if (property === void 0) return { type: "none" };
|
|
13
|
+
return {
|
|
14
|
+
type: "property",
|
|
15
|
+
propertyId: groupBy,
|
|
16
|
+
property,
|
|
17
|
+
options: schema.getPropertyOptions(property) ?? []
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const getKanbanGroupingPropertyId = (grouping) => {
|
|
21
|
+
switch (grouping.type) {
|
|
22
|
+
case "none": return null;
|
|
23
|
+
case "built-in":
|
|
24
|
+
case "property": return grouping.propertyId;
|
|
25
|
+
default: return grouping;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A grouping with no columns is not a board. A built-in grouping declares its
|
|
30
|
+
* columns up front, so an empty declaration is the signal that the board cannot
|
|
31
|
+
* be drawn; a property grouping always draws, even when the property has no
|
|
32
|
+
* options yet, because rows still land in the uncategorized bucket.
|
|
33
|
+
*/
|
|
34
|
+
const isKanbanGroupingRenderable = (grouping) => {
|
|
35
|
+
switch (grouping.type) {
|
|
36
|
+
case "none": return false;
|
|
37
|
+
case "built-in": return grouping.group.options.length > 0;
|
|
38
|
+
case "property": return true;
|
|
39
|
+
default: return grouping;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/** The rows a grouping can place on the board, in their incoming order. */
|
|
43
|
+
const selectKanbanRows = (rows, grouping) => {
|
|
44
|
+
switch (grouping.type) {
|
|
45
|
+
case "none": return [];
|
|
46
|
+
case "built-in": return grouping.group.selectRows ? grouping.group.selectRows(rows) : [...rows];
|
|
47
|
+
case "property": return [...rows];
|
|
48
|
+
default: return grouping;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/** The static column options for a grouping, excluding uncategorized. */
|
|
52
|
+
const resolveKanbanGroupOptions = (grouping) => {
|
|
53
|
+
switch (grouping.type) {
|
|
54
|
+
case "none": return [];
|
|
55
|
+
case "built-in": return grouping.group.options;
|
|
56
|
+
case "property": return grouping.options;
|
|
57
|
+
default: return grouping;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/** Append the uncategorized bucket (null value) after the options. */
|
|
61
|
+
const getKanbanGroups = (options, uncategorizedLabel) => {
|
|
62
|
+
const result = options.map((option) => ({
|
|
63
|
+
value: option.value,
|
|
64
|
+
label: option.label,
|
|
65
|
+
color: option.color,
|
|
66
|
+
colorBg: option.colorBg,
|
|
67
|
+
optionColor: option.optionColor
|
|
68
|
+
}));
|
|
69
|
+
result.push({
|
|
70
|
+
value: null,
|
|
71
|
+
label: uncategorizedLabel
|
|
72
|
+
});
|
|
73
|
+
return result;
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
export { getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { KanbanCardFieldSelection, selectKanbanCardFieldIds } from "./card-properties.js";
|
|
2
|
+
import { KanbanCardShell, KanbanCardShellProps } from "./card-shell.js";
|
|
3
|
+
import { KanbanColumnHeader, KanbanColumnHeaderProps } from "./column-header.js";
|
|
4
|
+
import { KanbanBuiltInGroup, KanbanGroup, KanbanGroupOption, KanbanGrouping, KanbanSchema, ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows } from "./grouping.js";
|
|
5
|
+
export { type KanbanBuiltInGroup, type KanbanCardFieldSelection, KanbanCardShell, type KanbanCardShellProps, KanbanColumnHeader, type KanbanColumnHeaderProps, type KanbanGroup, type KanbanGroupOption, type KanbanGrouping, type KanbanSchema, type ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanCardFieldIds, selectKanbanRows };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { selectKanbanCardFieldIds } from "./card-properties.js";
|
|
2
|
+
import { KanbanCardShell } from "./card-shell.js";
|
|
3
|
+
import { KanbanColumnHeader } from "./column-header.js";
|
|
4
|
+
import { getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows } from "./grouping.js";
|
|
5
|
+
export { KanbanCardShell, KanbanColumnHeader, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanCardFieldIds, selectKanbanRows };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
//#region src/lib/button-disposition.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* How a button expresses "unavailable".
|
|
5
|
+
*
|
|
6
|
+
* `buttonVariants` carries `disabled:pointer-events-none`, and the browser
|
|
7
|
+
* suppresses pointer and focus events on a natively disabled control anyway, so
|
|
8
|
+
* a disabled button never receives hover or focus. A tooltip attached to it can
|
|
9
|
+
* therefore never open, which is exactly when the user most needs to be told
|
|
10
|
+
* why the action is unavailable. When there is a tooltip to show, the button
|
|
11
|
+
* switches to the ARIA accessible-disabled pattern instead: it stays in the tab
|
|
12
|
+
* order and keeps receiving pointer events, while every activation path is
|
|
13
|
+
* blocked (see `blockDisabledActivation`).
|
|
14
|
+
*
|
|
15
|
+
* This mirrors Base UI's own `focusableWhenDisabled` disposition
|
|
16
|
+
* (`useFocusableWhenDisabled`): `aria-disabled` instead of `disabled`, focusable,
|
|
17
|
+
* and non-`Tab` key defaults suppressed.
|
|
18
|
+
*/
|
|
19
|
+
declare const BUTTON_DISPOSITION: {
|
|
20
|
+
readonly enabled: "enabled";
|
|
21
|
+
/** Native `disabled`: out of the tab order, inert to pointer and keyboard. */
|
|
22
|
+
readonly nativeDisabled: "native-disabled";
|
|
23
|
+
/** `aria-disabled`: focusable and hoverable so its tooltip can still open. */
|
|
24
|
+
readonly accessibleDisabled: "accessible-disabled";
|
|
25
|
+
};
|
|
26
|
+
type ButtonDisposition = (typeof BUTTON_DISPOSITION)[keyof typeof BUTTON_DISPOSITION];
|
|
27
|
+
type ButtonDispositionInput = {
|
|
28
|
+
disabled: boolean | undefined;
|
|
29
|
+
loading: boolean | undefined;
|
|
30
|
+
/** Resolved tooltip content, i.e. what `renderTooltipTrigger` would mount. */
|
|
31
|
+
tooltip: React$1.ReactNode;
|
|
32
|
+
};
|
|
33
|
+
declare const resolveButtonDisposition: ({ disabled, loading, tooltip }: ButtonDispositionInput) => ButtonDisposition;
|
|
34
|
+
/**
|
|
35
|
+
* The parts of a React synthetic event these blockers touch.
|
|
36
|
+
*
|
|
37
|
+
* `preventBaseUIHandler` is added by Base UI's `mergeProps` to the rightmost
|
|
38
|
+
* handler in a merged chain; calling it stops the handlers merged to its left,
|
|
39
|
+
* which is how a caller's own `onClick` is suppressed.
|
|
40
|
+
*/
|
|
41
|
+
type BlockableEvent = {
|
|
42
|
+
preventDefault: () => void;
|
|
43
|
+
preventBaseUIHandler?: (() => void) | undefined;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Swallow an activation on an `aria-disabled` control.
|
|
47
|
+
*
|
|
48
|
+
* `preventDefault()` is what keeps a `type="submit"` button from submitting its
|
|
49
|
+
* form: the browser's form-submission default runs off the click event, and
|
|
50
|
+
* implicit submission (Enter in a text field) dispatches a click at the form's
|
|
51
|
+
* default button, so both paths funnel through here.
|
|
52
|
+
*/
|
|
53
|
+
declare const blockDisabledActivation: (event: BlockableEvent) => void;
|
|
54
|
+
/**
|
|
55
|
+
* The keyboard counterpart. `Tab` keeps its default so focus can leave the
|
|
56
|
+
* control; suppressing the default on `Enter`/`Space` stops the browser from
|
|
57
|
+
* synthesizing the click that would activate or submit.
|
|
58
|
+
*/
|
|
59
|
+
declare const blockDisabledKeyActivation: (event: BlockableEvent & {
|
|
60
|
+
key: string;
|
|
61
|
+
}) => void;
|
|
62
|
+
//#endregion
|
|
63
|
+
export { BUTTON_DISPOSITION, ButtonDisposition, blockDisabledActivation, blockDisabledKeyActivation, resolveButtonDisposition };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { hasTooltipContent } from "./tooltip-content.js";
|
|
2
|
+
//#region src/lib/button-disposition.ts
|
|
3
|
+
/**
|
|
4
|
+
* How a button expresses "unavailable".
|
|
5
|
+
*
|
|
6
|
+
* `buttonVariants` carries `disabled:pointer-events-none`, and the browser
|
|
7
|
+
* suppresses pointer and focus events on a natively disabled control anyway, so
|
|
8
|
+
* a disabled button never receives hover or focus. A tooltip attached to it can
|
|
9
|
+
* therefore never open, which is exactly when the user most needs to be told
|
|
10
|
+
* why the action is unavailable. When there is a tooltip to show, the button
|
|
11
|
+
* switches to the ARIA accessible-disabled pattern instead: it stays in the tab
|
|
12
|
+
* order and keeps receiving pointer events, while every activation path is
|
|
13
|
+
* blocked (see `blockDisabledActivation`).
|
|
14
|
+
*
|
|
15
|
+
* This mirrors Base UI's own `focusableWhenDisabled` disposition
|
|
16
|
+
* (`useFocusableWhenDisabled`): `aria-disabled` instead of `disabled`, focusable,
|
|
17
|
+
* and non-`Tab` key defaults suppressed.
|
|
18
|
+
*/
|
|
19
|
+
const BUTTON_DISPOSITION = {
|
|
20
|
+
enabled: "enabled",
|
|
21
|
+
/** Native `disabled`: out of the tab order, inert to pointer and keyboard. */
|
|
22
|
+
nativeDisabled: "native-disabled",
|
|
23
|
+
/** `aria-disabled`: focusable and hoverable so its tooltip can still open. */
|
|
24
|
+
accessibleDisabled: "accessible-disabled"
|
|
25
|
+
};
|
|
26
|
+
const resolveButtonDisposition = ({ disabled, loading, tooltip }) => {
|
|
27
|
+
if (disabled !== true && loading !== true) return BUTTON_DISPOSITION.enabled;
|
|
28
|
+
return hasTooltipContent(tooltip) ? BUTTON_DISPOSITION.accessibleDisabled : BUTTON_DISPOSITION.nativeDisabled;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Swallow an activation on an `aria-disabled` control.
|
|
32
|
+
*
|
|
33
|
+
* `preventDefault()` is what keeps a `type="submit"` button from submitting its
|
|
34
|
+
* form: the browser's form-submission default runs off the click event, and
|
|
35
|
+
* implicit submission (Enter in a text field) dispatches a click at the form's
|
|
36
|
+
* default button, so both paths funnel through here.
|
|
37
|
+
*/
|
|
38
|
+
const blockDisabledActivation = (event) => {
|
|
39
|
+
event.preventBaseUIHandler?.();
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The keyboard counterpart. `Tab` keeps its default so focus can leave the
|
|
44
|
+
* control; suppressing the default on `Enter`/`Space` stops the browser from
|
|
45
|
+
* synthesizing the click that would activate or submit.
|
|
46
|
+
*/
|
|
47
|
+
const blockDisabledKeyActivation = (event) => {
|
|
48
|
+
event.preventBaseUIHandler?.();
|
|
49
|
+
if (event.key !== "Tab") event.preventDefault();
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export { BUTTON_DISPOSITION, blockDisabledActivation, blockDisabledKeyActivation, resolveButtonDisposition };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/lib/option-color.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The kit's colour token for user-chosen option colours.
|
|
4
|
+
*
|
|
5
|
+
* A token is either one of the sixteen named presets, which resolve to the
|
|
6
|
+
* theme's `--option-*` custom properties, or a six-character hex string, which
|
|
7
|
+
* resolves to a `color-mix` against the current background and foreground. The
|
|
8
|
+
* indirection is what keeps a stored colour theme-aware: nothing persists a
|
|
9
|
+
* literal colour the palette cannot re-tint.
|
|
10
|
+
*
|
|
11
|
+
* This lives in the kit rather than beside a data model because the resolver is
|
|
12
|
+
* pure presentation: it maps a token to CSS, and knows nothing about what the
|
|
13
|
+
* token was chosen for.
|
|
14
|
+
*/
|
|
15
|
+
/** Named preset or arbitrary 6-character hex colour (e.g. "FF0000"). */
|
|
16
|
+
type OptionColor = "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "gray" | (string & Record<never, never>);
|
|
17
|
+
type ColorVariants = {
|
|
18
|
+
background: string;
|
|
19
|
+
foreground: string;
|
|
20
|
+
color: string;
|
|
21
|
+
};
|
|
22
|
+
declare const emptyColor: ColorVariants;
|
|
23
|
+
/** Resolve any OptionColor (named or hex) to CSS color variants. */
|
|
24
|
+
declare const resolveOptionColor: (color: OptionColor) => ColorVariants;
|
|
25
|
+
/** The 16 named preset color keys. */
|
|
26
|
+
declare const optionColors: readonly OptionColor[];
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ColorVariants, OptionColor, emptyColor, optionColors, resolveOptionColor };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/lib/option-color.ts
|
|
2
|
+
/** Named preset colors with CSS variable references. */
|
|
3
|
+
const NAMED_COLORS = Object.freeze([
|
|
4
|
+
"red",
|
|
5
|
+
"orange",
|
|
6
|
+
"amber",
|
|
7
|
+
"yellow",
|
|
8
|
+
"lime",
|
|
9
|
+
"green",
|
|
10
|
+
"emerald",
|
|
11
|
+
"teal",
|
|
12
|
+
"cyan",
|
|
13
|
+
"sky",
|
|
14
|
+
"blue",
|
|
15
|
+
"indigo",
|
|
16
|
+
"violet",
|
|
17
|
+
"purple",
|
|
18
|
+
"fuchsia",
|
|
19
|
+
"gray"
|
|
20
|
+
]);
|
|
21
|
+
const optionVar = (name) => ({
|
|
22
|
+
background: `var(--option-${name}-bg)`,
|
|
23
|
+
foreground: `var(--option-${name}-fg)`,
|
|
24
|
+
color: `var(--option-${name})`
|
|
25
|
+
});
|
|
26
|
+
const hexVar = (hex) => ({
|
|
27
|
+
background: `color-mix(in srgb, #${hex} 12%, var(--background))`,
|
|
28
|
+
foreground: `color-mix(in srgb, #${hex} 50%, var(--foreground))`,
|
|
29
|
+
color: `#${hex}`
|
|
30
|
+
});
|
|
31
|
+
const namedColorsMap = {
|
|
32
|
+
red: optionVar("red"),
|
|
33
|
+
orange: optionVar("orange"),
|
|
34
|
+
amber: optionVar("amber"),
|
|
35
|
+
yellow: optionVar("yellow"),
|
|
36
|
+
lime: optionVar("lime"),
|
|
37
|
+
green: optionVar("green"),
|
|
38
|
+
emerald: optionVar("emerald"),
|
|
39
|
+
teal: optionVar("teal"),
|
|
40
|
+
cyan: optionVar("cyan"),
|
|
41
|
+
sky: optionVar("sky"),
|
|
42
|
+
blue: optionVar("blue"),
|
|
43
|
+
indigo: optionVar("indigo"),
|
|
44
|
+
violet: optionVar("violet"),
|
|
45
|
+
purple: optionVar("purple"),
|
|
46
|
+
fuchsia: optionVar("fuchsia"),
|
|
47
|
+
gray: optionVar("gray")
|
|
48
|
+
};
|
|
49
|
+
const emptyColor = optionVar("empty");
|
|
50
|
+
/** Resolve any OptionColor (named or hex) to CSS color variants. */
|
|
51
|
+
const resolveOptionColor = (color) => {
|
|
52
|
+
const namedColor = NAMED_COLORS.find((candidate) => candidate === color);
|
|
53
|
+
if (namedColor !== void 0) return namedColorsMap[namedColor];
|
|
54
|
+
return hexVar(color);
|
|
55
|
+
};
|
|
56
|
+
/** The 16 named preset color keys. */
|
|
57
|
+
const optionColors = NAMED_COLORS;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { emptyColor, optionColors, resolveOptionColor };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/lib/overlay-layer.d.ts
|
|
2
|
+
declare const OVERLAY_LAYER_CLASS_NAMES: {
|
|
3
|
+
/** Docked composer, suggestion chips, and other in-pane floating chrome. */
|
|
4
|
+
readonly chrome: "z-[80]";
|
|
5
|
+
/** Dialogs, sheets, popovers, tooltips: above chrome, below toasts. */
|
|
6
|
+
readonly default: "z-[85]";
|
|
7
|
+
/** Search sits a hair above a plain dialog so its own popup wins. */
|
|
8
|
+
readonly search: "z-[86]";
|
|
9
|
+
/**
|
|
10
|
+
* Transient popups — menu, select, combobox, context menus. Above everything
|
|
11
|
+
* including the toast viewport (z-[90]), because any of them can be opened
|
|
12
|
+
* *from* a dialog, a popover or a toast action and must cover its opener.
|
|
13
|
+
* They sat at z-50 alongside dialogs, so which one won was decided by portal
|
|
14
|
+
* mount order: a select inside a filter popover rendered behind it.
|
|
15
|
+
*/
|
|
16
|
+
readonly popup: "z-[100]";
|
|
17
|
+
/** Historical alias for `popup`; Search's own menus use the same band. */
|
|
18
|
+
readonly "search-child": "z-[100]";
|
|
19
|
+
};
|
|
20
|
+
type OverlayLayer = keyof typeof OVERLAY_LAYER_CLASS_NAMES;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { OVERLAY_LAYER_CLASS_NAMES, OverlayLayer };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/lib/overlay-layer.ts
|
|
2
|
+
const OVERLAY_LAYER_CLASS_NAMES = {
|
|
3
|
+
/** Docked composer, suggestion chips, and other in-pane floating chrome. */
|
|
4
|
+
chrome: "z-[80]",
|
|
5
|
+
/** Dialogs, sheets, popovers, tooltips: above chrome, below toasts. */
|
|
6
|
+
default: "z-[85]",
|
|
7
|
+
/** Search sits a hair above a plain dialog so its own popup wins. */
|
|
8
|
+
search: "z-[86]",
|
|
9
|
+
/**
|
|
10
|
+
* Transient popups — menu, select, combobox, context menus. Above everything
|
|
11
|
+
* including the toast viewport (z-[90]), because any of them can be opened
|
|
12
|
+
* *from* a dialog, a popover or a toast action and must cover its opener.
|
|
13
|
+
* They sat at z-50 alongside dialogs, so which one won was decided by portal
|
|
14
|
+
* mount order: a select inside a filter popover rendered behind it.
|
|
15
|
+
*/
|
|
16
|
+
popup: "z-[100]",
|
|
17
|
+
/** Historical alias for `popup`; Search's own menus use the same band. */
|
|
18
|
+
"search-child": "z-[100]"
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { OVERLAY_LAYER_CLASS_NAMES };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/lib/tab-order.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether a committed tab list holds different elements, or the same elements
|
|
4
|
+
* in a different order, than the previous commit.
|
|
5
|
+
*
|
|
6
|
+
* Base UI measures the tabs indicator during render, so it reads the DOM as it
|
|
7
|
+
* stood before the commit. Its correction is a `ResizeObserver`, which catches
|
|
8
|
+
* an insertion or a removal (the list's own width changes) but not a reorder:
|
|
9
|
+
* there the tabs swap places without anything changing size. Detecting that is
|
|
10
|
+
* the whole of the workaround, so compare identity rather than count, because
|
|
11
|
+
* React moves the same nodes when it reorders keyed children.
|
|
12
|
+
*/
|
|
13
|
+
declare const hasTabOrderChanged: <T>(previous: readonly T[], next: readonly T[]) => boolean;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { hasTabOrderChanged };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/lib/tab-order.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether a committed tab list holds different elements, or the same elements
|
|
4
|
+
* in a different order, than the previous commit.
|
|
5
|
+
*
|
|
6
|
+
* Base UI measures the tabs indicator during render, so it reads the DOM as it
|
|
7
|
+
* stood before the commit. Its correction is a `ResizeObserver`, which catches
|
|
8
|
+
* an insertion or a removal (the list's own width changes) but not a reorder:
|
|
9
|
+
* there the tabs swap places without anything changing size. Detecting that is
|
|
10
|
+
* the whole of the workaround, so compare identity rather than count, because
|
|
11
|
+
* React moves the same nodes when it reorders keyed children.
|
|
12
|
+
*/
|
|
13
|
+
const hasTabOrderChanged = (previous, next) => previous.length !== next.length || next.some((tab, index) => tab !== previous[index]);
|
|
14
|
+
//#endregion
|
|
15
|
+
export { hasTabOrderChanged };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
//#region src/lib/tooltip-content.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether a tooltip value would actually render a popup.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for "there is a tooltip": `renderTooltipTrigger`
|
|
7
|
+
* decides whether to mount one, and `resolveButtonDisposition` decides whether
|
|
8
|
+
* a disabled button has to stay hoverable so that popup can open. Two copies of
|
|
9
|
+
* this predicate would drift into a button that suppresses its own tooltip.
|
|
10
|
+
*/
|
|
11
|
+
declare const hasTooltipContent: (content: React$1.ReactNode) => boolean;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { hasTooltipContent };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/lib/tooltip-content.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether a tooltip value would actually render a popup.
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth for "there is a tooltip": `renderTooltipTrigger`
|
|
6
|
+
* decides whether to mount one, and `resolveButtonDisposition` decides whether
|
|
7
|
+
* a disabled button has to stay hoverable so that popup can open. Two copies of
|
|
8
|
+
* this predicate would drift into a button that suppresses its own tooltip.
|
|
9
|
+
*/
|
|
10
|
+
const hasTooltipContent = (content) => content !== void 0 && content !== null && content !== "" && content !== false;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { hasTooltipContent };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ClassValue } from "clsx";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
//#region src/lib/utils.d.ts
|
|
4
|
+
declare function cn(...inputs: ClassValue[]): string;
|
|
5
|
+
/**
|
|
6
|
+
* Compose multiple refs into a single ref callback.
|
|
7
|
+
* Adapted from `@radix-ui/react-compose-refs` (MIT).
|
|
8
|
+
*
|
|
9
|
+
* Collects cleanup functions returned by React 19 ref
|
|
10
|
+
* callbacks and returns a combined cleanup so React can
|
|
11
|
+
* invoke it on unmount instead of re-calling with `null`.
|
|
12
|
+
*
|
|
13
|
+
* Use this rather than merging refs inline: forwarding a
|
|
14
|
+
* caller's return value verbatim makes React read whatever
|
|
15
|
+
* their callback happened to return as a cleanup, and warn
|
|
16
|
+
* when it is neither a function nor `undefined`.
|
|
17
|
+
*/
|
|
18
|
+
declare const composeRefs: <T>(...refs: (React$1.Ref<T> | undefined)[]) => ((node: T | null) => (() => void) | undefined);
|
|
19
|
+
//#endregion
|
|
20
|
+
export { cn, composeRefs };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
//#region src/lib/utils.ts
|
|
4
|
+
function cn(...inputs) {
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Compose multiple refs into a single ref callback.
|
|
9
|
+
* Adapted from `@radix-ui/react-compose-refs` (MIT).
|
|
10
|
+
*
|
|
11
|
+
* Collects cleanup functions returned by React 19 ref
|
|
12
|
+
* callbacks and returns a combined cleanup so React can
|
|
13
|
+
* invoke it on unmount instead of re-calling with `null`.
|
|
14
|
+
*
|
|
15
|
+
* Use this rather than merging refs inline: forwarding a
|
|
16
|
+
* caller's return value verbatim makes React read whatever
|
|
17
|
+
* their callback happened to return as a cleanup, and warn
|
|
18
|
+
* when it is neither a function nor `undefined`.
|
|
19
|
+
*/
|
|
20
|
+
const composeRefs = (...refs) => (node) => {
|
|
21
|
+
const cleanups = [];
|
|
22
|
+
for (const ref of refs) if (typeof ref === "function") {
|
|
23
|
+
const cleanup = ref(node);
|
|
24
|
+
if (typeof cleanup === "function") cleanups.push(() => {
|
|
25
|
+
cleanup();
|
|
26
|
+
});
|
|
27
|
+
else if (node !== null) cleanups.push(() => {
|
|
28
|
+
ref(null);
|
|
29
|
+
});
|
|
30
|
+
} else if (ref !== void 0 && ref !== null) {
|
|
31
|
+
ref.current = node;
|
|
32
|
+
if (node !== null) cleanups.push(() => {
|
|
33
|
+
ref.current = null;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (cleanups.length > 0) return () => {
|
|
37
|
+
for (const cleanup of cleanups) cleanup();
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { cn, composeRefs };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/lib/week.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Locale week data (first weekday, weekend) derived from CLDR via Intl.
|
|
4
|
+
* Shared so the app shell and the date picker resolve week starts identically.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Read CLDR week info for a locale, tolerating the legacy `weekInfo` accessor
|
|
8
|
+
* on engines that predate `getWeekInfo()`. Returns undefined when unavailable
|
|
9
|
+
* so callers can apply their own fallback.
|
|
10
|
+
*/
|
|
11
|
+
declare const getLocaleWeekInfo: (locale: string) => Intl.WeekInfo | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* First weekday as a `Date.getDay()` value (0 = Sunday … 6 = Saturday): Monday
|
|
14
|
+
* across most of Europe, Sunday in the US, Saturday across much of the Gulf.
|
|
15
|
+
* Falls back to Monday when the runtime lacks week info.
|
|
16
|
+
*/
|
|
17
|
+
declare const getFirstWeekday: (locale: string) => number;
|
|
18
|
+
/**
|
|
19
|
+
* Weekend weekdays as `Date.getDay()` values (0 = Sunday … 6 = Saturday):
|
|
20
|
+
* Saturday/Sunday across the West, Friday/Saturday across much of the Gulf.
|
|
21
|
+
* Falls back to Saturday/Sunday when the runtime lacks week info.
|
|
22
|
+
*/
|
|
23
|
+
declare const getWeekendDays: (locale: string) => ReadonlySet<number>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { getFirstWeekday, getLocaleWeekInfo, getWeekendDays };
|