@mastra/playground-ui 68.0.0-alpha.1 → 68.0.0-alpha.2
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/dist/{DataCodeSection-CcPmJPTp.cjs → DataCodeSection-CFxXsQWH.cjs} +3 -3
- package/dist/DataCodeSection-CFxXsQWH.cjs.map +1 -0
- package/dist/{DataCodeSection-KJRyRD1Q.js → DataCodeSection-PY9XEg2C.js} +3 -3
- package/dist/DataCodeSection-PY9XEg2C.js.map +1 -0
- package/dist/{DataPanel-BwB1Oifd.cjs → DataPanel-Coc9h35I.cjs} +2 -2
- package/dist/{DataPanel-BwB1Oifd.cjs.map → DataPanel-Coc9h35I.cjs.map} +1 -1
- package/dist/{DataPanel-Bv4M17xO.js → DataPanel-IgB9D5PO.js} +2 -2
- package/dist/{DataPanel-Bv4M17xO.js.map → DataPanel-IgB9D5PO.js.map} +1 -1
- package/dist/{FilterBar-BO-vvvyq.js → FilterBar-BgW2dqRQ.js} +7 -7
- package/dist/FilterBar-BgW2dqRQ.js.map +1 -0
- package/dist/{FilterBar-Buw0tv51.cjs → FilterBar-CQl04O6t.cjs} +7 -7
- package/dist/FilterBar-CQl04O6t.cjs.map +1 -0
- package/dist/{MetricsTable-D-aZf3dk.cjs → MetricsTable-BSl81oNe.cjs} +44 -9
- package/dist/MetricsTable-BSl81oNe.cjs.map +1 -0
- package/dist/{MetricsTable-CRPeLHe1.js → MetricsTable-DID6LRoP.js} +44 -9
- package/dist/MetricsTable-DID6LRoP.js.map +1 -0
- package/dist/{ask-user-DdYz56qH.cjs → ask-user-C1p2PEYe.cjs} +2 -2
- package/dist/ask-user-C1p2PEYe.cjs.map +1 -0
- package/dist/{ask-user-B225wzWc.js → ask-user-DPznrqGO.js} +2 -2
- package/dist/ask-user-DPznrqGO.js.map +1 -0
- package/dist/chart-plot.css +5 -4
- package/dist/{combobox-CtCK0mNx.js → combobox-D21no8t5.js} +4 -4
- package/dist/{combobox-CtCK0mNx.js.map → combobox-D21no8t5.js.map} +1 -1
- package/dist/{combobox-CMx2ayFN.cjs → combobox-DIT_6xiG.cjs} +4 -4
- package/dist/combobox-DIT_6xiG.cjs.map +1 -0
- package/dist/{combobox-parts-ScKcYjMR.cjs → combobox-parts-C4phOq4t.cjs} +2 -2
- package/dist/combobox-parts-C4phOq4t.cjs.map +1 -0
- package/dist/{combobox-parts-C1CugUAU.js → combobox-parts-D90ir0Py.js} +3 -3
- package/dist/combobox-parts-D90ir0Py.js.map +1 -0
- package/dist/components/Combobox.cjs.js +2 -2
- package/dist/components/Combobox.es.js +2 -2
- package/dist/components/Command.cjs.js +1 -1
- package/dist/components/Command.cjs.js.map +1 -1
- package/dist/components/Command.es.js +1 -1
- package/dist/components/Command.es.js.map +1 -1
- package/dist/components/DataCodeSection.cjs.js +1 -1
- package/dist/components/DataCodeSection.es.js +1 -1
- package/dist/components/DataList.cjs.js +1 -1
- package/dist/components/DataList.es.js +1 -1
- package/dist/components/DataPanel.cjs.js +1 -1
- package/dist/components/DataPanel.es.js +1 -1
- package/dist/components/DateTimePicker.cjs.js +1 -1
- package/dist/components/DateTimePicker.cjs.js.map +1 -1
- package/dist/components/DateTimePicker.es.js +1 -1
- package/dist/components/DateTimePicker.es.js.map +1 -1
- package/dist/components/FilterBar.cjs.js +3 -3
- package/dist/components/FilterBar.es.js +3 -3
- package/dist/components/FormFieldBlocks.cjs.js +1 -1
- package/dist/components/FormFieldBlocks.cjs.js.map +1 -1
- package/dist/components/FormFieldBlocks.es.js +1 -1
- package/dist/components/FormFieldBlocks.es.js.map +1 -1
- package/dist/components/MetricsTable.cjs.js +1 -1
- package/dist/components/MetricsTable.es.js +1 -1
- package/dist/components/PropertyFilter.cjs.js +3 -3
- package/dist/components/PropertyFilter.cjs.js.map +1 -1
- package/dist/components/PropertyFilter.es.js +3 -3
- package/dist/components/PropertyFilter.es.js.map +1 -1
- package/dist/components/SavedViews.cjs.js +1 -1
- package/dist/components/SavedViews.es.js +1 -1
- package/dist/components/Sidebar.cjs.js +8 -8
- package/dist/components/Sidebar.cjs.js.map +1 -1
- package/dist/components/Sidebar.es.js +9 -9
- package/dist/components/Sidebar.es.js.map +1 -1
- package/dist/components/ai/ask-user.cjs.js +1 -1
- package/dist/components/ai/ask-user.es.js +1 -1
- package/dist/domains/chat/tools/badges/ask-user-badge.cjs.js +1 -1
- package/dist/domains/chat/tools/badges/ask-user-badge.es.js +1 -1
- package/dist/domains/dashboards/kinds/table/table-view.cjs.js +1 -1
- package/dist/domains/dashboards/kinds/table/table-view.es.js +1 -1
- package/dist/domains/dashboards/metrics-dashboard.cjs.js +1 -1
- package/dist/domains/dashboards/metrics-dashboard.cjs.js.map +1 -1
- package/dist/domains/dashboards/metrics-dashboard.es.js +1 -1
- package/dist/domains/dashboards/metrics-dashboard.es.js.map +1 -1
- package/dist/domains/datasets/components/save-as-dataset-item-dialog.cjs.js +1 -1
- package/dist/domains/datasets/components/save-as-dataset-item-dialog.es.js +1 -1
- package/dist/domains/logs/components/log-data-panel.cjs.js +1 -1
- package/dist/domains/logs/components/log-data-panel.es.js +1 -1
- package/dist/domains/logs/components/logs-saved-views.cjs.js +1 -1
- package/dist/domains/logs/components/logs-saved-views.es.js +1 -1
- package/dist/domains/logs/components/logs-toolbar.cjs.js +1 -1
- package/dist/domains/logs/components/logs-toolbar.es.js +1 -1
- package/dist/domains/metrics/components/breakdown/breakdown-table.cjs.js +2 -2
- package/dist/domains/metrics/components/breakdown/breakdown-table.cjs.js.map +1 -1
- package/dist/domains/metrics/components/breakdown/breakdown-table.es.js +2 -2
- package/dist/domains/metrics/components/breakdown/breakdown-table.es.js.map +1 -1
- package/dist/domains/metrics/components/metrics-time-range-chip.cjs.js +1 -1
- package/dist/domains/metrics/components/metrics-time-range-chip.es.js +1 -1
- package/dist/domains/observability/saved-views/observability-saved-views.cjs.js +1 -1
- package/dist/domains/observability/saved-views/observability-saved-views.es.js +1 -1
- package/dist/domains/observability/saved-views.cjs.js +1 -1
- package/dist/domains/observability/saved-views.es.js +1 -1
- package/dist/domains/observability.cjs.js +1 -1
- package/dist/domains/observability.es.js +1 -1
- package/dist/domains/scores/components/score-data-panel.cjs.js +1 -1
- package/dist/domains/scores/components/score-data-panel.es.js +1 -1
- package/dist/domains/scores/components/span-scoring.cjs.js +3 -3
- package/dist/domains/scores/components/span-scoring.cjs.js.map +1 -1
- package/dist/domains/scores/components/span-scoring.es.js +3 -3
- package/dist/domains/scores/components/span-scoring.es.js.map +1 -1
- package/dist/domains/skills/skill-simple-form.cjs.js +1 -1
- package/dist/domains/skills/skill-simple-form.cjs.js.map +1 -1
- package/dist/domains/skills/skill-simple-form.es.js +1 -1
- package/dist/domains/skills/skill-simple-form.es.js.map +1 -1
- package/dist/domains/spans/components/spans-explorer-body.cjs.js +1 -1
- package/dist/domains/spans/components/spans-explorer-body.es.js +1 -1
- package/dist/domains/spans/components/spans-filter-bar.cjs.js +3 -3
- package/dist/domains/spans/components/spans-filter-bar.cjs.js.map +1 -1
- package/dist/domains/spans/components/spans-filter-bar.es.js +3 -3
- package/dist/domains/spans/components/spans-filter-bar.es.js.map +1 -1
- package/dist/domains/spans/hooks/use-spans-filter-bar.cjs.js +1 -1
- package/dist/domains/spans/hooks/use-spans-filter-bar.es.js +1 -1
- package/dist/domains/threads/components/threads-explorer-body.cjs.js +1 -1
- package/dist/domains/threads/components/threads-explorer-body.es.js +1 -1
- package/dist/domains/threads/components/threads-filter-bar.cjs.js +3 -3
- package/dist/domains/threads/components/threads-filter-bar.cjs.js.map +1 -1
- package/dist/domains/threads/components/threads-filter-bar.es.js +3 -3
- package/dist/domains/threads/components/threads-filter-bar.es.js.map +1 -1
- package/dist/domains/threads/hooks/use-threads-filter-bar.cjs.js +1 -1
- package/dist/domains/threads/hooks/use-threads-filter-bar.es.js +1 -1
- package/dist/domains/traces/components/feedback-thread.cjs.js +1 -1
- package/dist/domains/traces/components/feedback-thread.cjs.js.map +1 -1
- package/dist/domains/traces/components/feedback-thread.es.js +1 -1
- package/dist/domains/traces/components/feedback-thread.es.js.map +1 -1
- package/dist/domains/traces/components/span-data-panel-view.cjs.js +1 -1
- package/dist/domains/traces/components/span-data-panel-view.es.js +1 -1
- package/dist/domains/traces/components/span-summary-description.cjs.js +1 -1
- package/dist/domains/traces/components/span-summary-description.es.js +1 -1
- package/dist/domains/traces/components/thread-trace/thread-trace-details.cjs.js +1 -1
- package/dist/domains/traces/components/thread-trace/thread-trace-details.es.js +1 -1
- package/dist/domains/traces/components/trace-columns-menu.cjs.js +3 -3
- package/dist/domains/traces/components/trace-columns-menu.cjs.js.map +1 -1
- package/dist/domains/traces/components/trace-columns-menu.es.js +3 -3
- package/dist/domains/traces/components/trace-columns-menu.es.js.map +1 -1
- package/dist/domains/traces/components/trace-data-panel-view.cjs.js +2 -2
- package/dist/domains/traces/components/trace-data-panel-view.cjs.js.map +1 -1
- package/dist/domains/traces/components/trace-data-panel-view.es.js +2 -2
- package/dist/domains/traces/components/trace-data-panel-view.es.js.map +1 -1
- package/dist/domains/traces/components/trace-id-button.cjs.js +1 -1
- package/dist/domains/traces/components/trace-id-button.es.js +1 -1
- package/dist/domains/traces/components/trace-summary-description.cjs.js +1 -1
- package/dist/domains/traces/components/trace-summary-description.es.js +1 -1
- package/dist/domains/traces/components/trace-thread-panel.cjs.js +1 -1
- package/dist/domains/traces/components/trace-thread-panel.es.js +1 -1
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.cjs.js +3 -3
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.cjs.js.map +1 -1
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.es.js +3 -3
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.es.js.map +1 -1
- package/dist/domains/traces/components/traces-explorer/traces-explorer-saved-views.cjs.js +1 -1
- package/dist/domains/traces/components/traces-explorer/traces-explorer-saved-views.es.js +1 -1
- package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.cjs.js +1 -1
- package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.es.js +1 -1
- package/dist/domains/workflows/components/workflow-combobox.cjs.js +2 -2
- package/dist/domains/workflows/components/workflow-combobox.cjs.js.map +1 -1
- package/dist/domains/workflows/components/workflow-combobox.es.js +2 -2
- package/dist/domains/workflows/components/workflow-combobox.es.js.map +1 -1
- package/dist/domains/workflows/workflow/input/workflow-processor-input.cjs.js +1 -1
- package/dist/domains/workflows/workflow/input/workflow-processor-input.cjs.js.map +1 -1
- package/dist/domains/workflows/workflow/input/workflow-processor-input.es.js +1 -1
- package/dist/domains/workflows/workflow/input/workflow-processor-input.es.js.map +1 -1
- package/dist/domains/workspace/components/workspace-add-skill-dialog.cjs.js +1 -1
- package/dist/domains/workspace/components/workspace-add-skill-dialog.cjs.js.map +1 -1
- package/dist/domains/workspace/components/workspace-add-skill-dialog.es.js +1 -1
- package/dist/domains/workspace/components/workspace-add-skill-dialog.es.js.map +1 -1
- package/dist/{filter-bar-chip-B4sV4gDm.js → filter-bar-chip-CABeKrtf.js} +4 -4
- package/dist/{filter-bar-chip-B4sV4gDm.js.map → filter-bar-chip-CABeKrtf.js.map} +1 -1
- package/dist/{filter-bar-chip-CIEsWcEw.cjs → filter-bar-chip-Cg-UbIwP.cjs} +4 -4
- package/dist/{filter-bar-chip-CIEsWcEw.cjs.map → filter-bar-chip-Cg-UbIwP.cjs.map} +1 -1
- package/dist/{filter-bar-time-range-DvuaFnkI.cjs → filter-bar-time-range-BA-pXMlo.cjs} +2 -2
- package/dist/{filter-bar-time-range-DvuaFnkI.cjs.map → filter-bar-time-range-BA-pXMlo.cjs.map} +1 -1
- package/dist/{filter-bar-time-range-CXQMBgpz.js → filter-bar-time-range-mg9JeYue.js} +2 -2
- package/dist/{filter-bar-time-range-CXQMBgpz.js.map → filter-bar-time-range-mg9JeYue.js.map} +1 -1
- package/dist/layout/tabbed-container.cjs.js +1 -1
- package/dist/layout/tabbed-container.es.js +1 -1
- package/dist/{observability-saved-views-BBam8Fr5.js → observability-saved-views-CjKTEp1V.js} +3 -3
- package/dist/observability-saved-views-CjKTEp1V.js.map +1 -0
- package/dist/{observability-saved-views-BAzxxoAr.cjs → observability-saved-views-Daizz4tU.cjs} +3 -3
- package/dist/observability-saved-views-Daizz4tU.cjs.map +1 -0
- package/dist/primitives/chart-plot.cjs.js +8 -6
- package/dist/primitives/chart-plot.cjs.js.map +1 -1
- package/dist/primitives/chart-plot.es.js +8 -6
- package/dist/primitives/chart-plot.es.js.map +1 -1
- package/dist/primitives/control-icon.cjs.js.map +1 -1
- package/dist/primitives/control-icon.es.js.map +1 -1
- package/dist/{property-filter-applied-p3dTTDdb.js → property-filter-applied--U5EZdaA.js} +2 -2
- package/dist/property-filter-applied--U5EZdaA.js.map +1 -0
- package/dist/{property-filter-applied-BCeRLcXb.cjs → property-filter-applied-BkkIObRq.cjs} +2 -2
- package/dist/property-filter-applied-BkkIObRq.cjs.map +1 -0
- package/dist/src/ds/components/MetricsTable/metrics-table-rows.d.ts +10 -3
- package/dist/src/ds/components/MetricsTable/metrics-table.d.ts +1 -1
- package/dist/style.css +14 -1
- package/dist/{tabbed-container-DQU7pVio.cjs → tabbed-container-B4lCBgRP.cjs} +2 -2
- package/dist/{tabbed-container-DQU7pVio.cjs.map → tabbed-container-B4lCBgRP.cjs.map} +1 -1
- package/dist/{tabbed-container-5j6dhoRX.js → tabbed-container-DI1GI1Qv.js} +2 -2
- package/dist/{tabbed-container-5j6dhoRX.js.map → tabbed-container-DI1GI1Qv.js.map} +1 -1
- package/oxlint/index.js +90 -0
- package/package.json +8 -8
- package/theme.css +4 -3
- package/dist/DataCodeSection-CcPmJPTp.cjs.map +0 -1
- package/dist/DataCodeSection-KJRyRD1Q.js.map +0 -1
- package/dist/FilterBar-BO-vvvyq.js.map +0 -1
- package/dist/FilterBar-Buw0tv51.cjs.map +0 -1
- package/dist/MetricsTable-CRPeLHe1.js.map +0 -1
- package/dist/MetricsTable-D-aZf3dk.cjs.map +0 -1
- package/dist/ask-user-B225wzWc.js.map +0 -1
- package/dist/ask-user-DdYz56qH.cjs.map +0 -1
- package/dist/combobox-CMx2ayFN.cjs.map +0 -1
- package/dist/combobox-parts-C1CugUAU.js.map +0 -1
- package/dist/combobox-parts-ScKcYjMR.cjs.map +0 -1
- package/dist/observability-saved-views-BAzxxoAr.cjs.map +0 -1
- package/dist/observability-saved-views-BBam8Fr5.js.map +0 -1
- package/dist/property-filter-applied-BCeRLcXb.cjs.map +0 -1
- package/dist/property-filter-applied-p3dTTDdb.js.map +0 -1
|
@@ -76,10 +76,17 @@ function MetricsTableHeaderLabel({ header, description, sorted, onSort }) {
|
|
|
76
76
|
}
|
|
77
77
|
// src/ds/components/MetricsTable/metrics-table-rows.tsx
|
|
78
78
|
/**
|
|
79
|
+
* Full-width lines that fade over their last 6px. A hovered row covers the lines above and below
|
|
80
|
+
* it, which then fade over 24px so they read as part of the hover. Written out in full so Tailwind
|
|
81
|
+
* finds every class.
|
|
82
|
+
*/
|
|
83
|
+
var ROW_LINE = "isolate before:absolute before:inset-x-0 before:-top-px before:bottom-0 before:-z-10 before:rounded hover:before:bg-fill-subtle after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border last:after:hidden after:[mask-image:linear-gradient(to_right,transparent,black_6px,black_calc(100%-6px),transparent)] hover:after:[mask-image:linear-gradient(to_right,transparent,black_24px,black_calc(100%-24px),transparent)] [&:has(+:hover)]:after:[mask-image:linear-gradient(to_right,transparent,black_24px,black_calc(100%-24px),transparent)]";
|
|
84
|
+
/**
|
|
79
85
|
* Rows are 34.5px: 7px above 13px text (a 19.5px line) and 8px below, one more pixel for the
|
|
80
86
|
* separator drawn in the row's last pixel, so text sits centered between the lines.
|
|
81
87
|
*/
|
|
82
|
-
var
|
|
88
|
+
var ROW_BOX = "pt-1.75 pb-2 text-body-sm";
|
|
89
|
+
var ROW = cn("relative col-span-full grid grid-cols-subgrid items-center px-3", ROW_BOX, ROW_LINE);
|
|
83
90
|
/** Skeleton widths for the first column's names, so loading rows don't look stamped. */
|
|
84
91
|
var SKELETON_NAMES = [
|
|
85
92
|
"w-32",
|
|
@@ -103,12 +110,24 @@ function skeletonBar(column, columnIndex, rowIndex) {
|
|
|
103
110
|
return cn("left-0", SKELETON_NAMES[rowIndex % SKELETON_NAMES.length]);
|
|
104
111
|
}
|
|
105
112
|
/** Skeleton rows while loading, the empty message without rows, else the page's rows. */
|
|
106
|
-
function MetricsTableBody({ columns, rows, getRowKey, empty,
|
|
107
|
-
if (isLoading) return Array.from({ length:
|
|
113
|
+
function MetricsTableBody({ columns, rows, getRowKey, empty, pageSize, isLoading, ...behavior }) {
|
|
114
|
+
if (isLoading) return Array.from({ length: pageSize ?? 5 }, (_, i) => /* @__PURE__ */ jsx(MetricsTableSkeletonRow, {
|
|
108
115
|
columns,
|
|
109
116
|
rowIndex: i
|
|
110
117
|
}, i));
|
|
111
118
|
if (rows.length === 0) {
|
|
119
|
+
if (pageSize) {
|
|
120
|
+
if (empty === void 0) return /* @__PURE__ */ jsx(MetricsTableFiller, { count: pageSize });
|
|
121
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
122
|
+
role: "row",
|
|
123
|
+
className: "col-span-full grid *:col-start-1 *:row-start-1",
|
|
124
|
+
children: [/* @__PURE__ */ jsx(MetricsTableFiller, { count: pageSize }), /* @__PURE__ */ jsx("p", {
|
|
125
|
+
role: "cell",
|
|
126
|
+
className: "flex items-center justify-center px-3 py-4 text-center text-body-sm text-muted-foreground",
|
|
127
|
+
children: empty
|
|
128
|
+
})]
|
|
129
|
+
});
|
|
130
|
+
}
|
|
112
131
|
if (empty === void 0) return null;
|
|
113
132
|
return /* @__PURE__ */ jsx("div", {
|
|
114
133
|
role: "row",
|
|
@@ -126,6 +145,22 @@ function MetricsTableBody({ columns, rows, getRowKey, empty, skeletonRows, isLoa
|
|
|
126
145
|
...behavior
|
|
127
146
|
}, getRowKey(row)));
|
|
128
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Blank space as tall as `count` rows, so a paged table is a full page tall however many rows it
|
|
150
|
+
* shows. Rows of the same box, not a fixed height, so it follows the row's text size.
|
|
151
|
+
*/
|
|
152
|
+
function MetricsTableFiller({ count }) {
|
|
153
|
+
if (count <= 0) return null;
|
|
154
|
+
return /* @__PURE__ */ jsx("div", {
|
|
155
|
+
"aria-hidden": true,
|
|
156
|
+
"data-slot": "metrics-table-filler",
|
|
157
|
+
className: "col-span-full",
|
|
158
|
+
children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx("div", {
|
|
159
|
+
className: ROW_BOX,
|
|
160
|
+
children: ""
|
|
161
|
+
}, i))
|
|
162
|
+
});
|
|
163
|
+
}
|
|
129
164
|
/** A loading row: the real row's markup with a bar in each cell, so it has the same height. */
|
|
130
165
|
function MetricsTableSkeletonRow({ columns, rowIndex }) {
|
|
131
166
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -222,7 +257,7 @@ var columnWidth = (column) => column.width ?? (column.align === "end" ? "minmax(
|
|
|
222
257
|
* A table for the numbers behind a chart: one row per series or group, columns for its figures,
|
|
223
258
|
* separated by lines that fade at the ends. With `SeriesMark` and `Sparkline` cells and the row
|
|
224
259
|
* hover and click hooks it doubles as the chart's legend; without them it's a plain data table.
|
|
225
|
-
* With `pageSize` it pages long lists. For a share of a total, a compact `ShareBar` fits better.
|
|
260
|
+
* With `pageSize` it pages long lists, and is always a full page tall. For a share of a total, a compact `ShareBar` fits better.
|
|
226
261
|
*/
|
|
227
262
|
function MetricsTable({ columns, rows, getRowKey, sort, defaultSort, onSortChange, onRowClick, onRowHover, isRowDimmed, isRowActive, rowTitle, empty, pageSize, isLoading = false, "aria-label": ariaLabel, className }) {
|
|
228
263
|
const [ownSort, setOwnSort] = useState(defaultSort);
|
|
@@ -242,9 +277,9 @@ function MetricsTable({ columns, rows, getRowKey, sort, defaultSort, onSortChang
|
|
|
242
277
|
};
|
|
243
278
|
return /* @__PURE__ */ jsxs("div", {
|
|
244
279
|
className: cn("grid min-w-0 gap-3", className),
|
|
245
|
-
children: [/* @__PURE__ */
|
|
280
|
+
children: [/* @__PURE__ */ jsxs(ScrollArea, {
|
|
246
281
|
orientation: "horizontal",
|
|
247
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
282
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
248
283
|
role: "table",
|
|
249
284
|
"aria-label": ariaLabel,
|
|
250
285
|
className: "grid min-w-min gap-x-4",
|
|
@@ -276,10 +311,10 @@ function MetricsTable({ columns, rows, getRowKey, sort, defaultSort, onSortChang
|
|
|
276
311
|
isRowActive,
|
|
277
312
|
rowTitle,
|
|
278
313
|
empty,
|
|
279
|
-
|
|
314
|
+
pageSize,
|
|
280
315
|
isLoading
|
|
281
316
|
})]
|
|
282
|
-
})
|
|
317
|
+
}), pageSize !== void 0 && !isLoading && shown.length > 0 && /* @__PURE__ */ jsx(MetricsTableFiller, { count: pageSize - shown.length })]
|
|
283
318
|
}), showsPagination && /* @__PURE__ */ jsx(TablePagination, {
|
|
284
319
|
page: safePage,
|
|
285
320
|
pageCount,
|
|
@@ -289,4 +324,4 @@ function MetricsTable({ columns, rows, getRowKey, sort, defaultSort, onSortChang
|
|
|
289
324
|
}
|
|
290
325
|
export { MetricsTable as t };
|
|
291
326
|
|
|
292
|
-
//# sourceMappingURL=MetricsTable-
|
|
327
|
+
//# sourceMappingURL=MetricsTable-DID6LRoP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricsTable-DID6LRoP.js","names":[],"sources":["../src/ds/components/MetricsTable/metrics-table-header.tsx","../src/ds/components/MetricsTable/metrics-table-rows.tsx","../src/ds/components/MetricsTable/metrics-table.tsx"],"sourcesContent":["import { ArrowDownIcon, ArrowUpIcon } from 'lucide-react';\nimport { useId } from 'react';\nimport type { ComponentProps, ReactNode } from 'react';\nimport type { MetricsTableSortDirection } from './metrics-table-types';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/ds/components/Tooltip';\nimport { cn } from '@/lib/utils';\n\nconst SORT_ICON = { asc: ArrowUpIcon, desc: ArrowDownIcon };\n\nexport const FOCUS =\n 'focus-visible:outline-1 focus-visible:outline-solid focus-visible:outline-border-focus focus-visible:-outline-offset-1';\n\n/**\n * A faint dotted underline under the label: the header explains itself on hover or focus. It\n * brightens while the header is hovered or focused, so it hints without pulling the eye at rest.\n */\nconst EXPLAINED =\n 'cursor-help underline decoration-dotted decoration-placeholder/45 underline-offset-4 transition-[text-decoration-color] duration-150 group-hover/header:decoration-placeholder group-focus-visible/header:decoration-placeholder';\n\ntype HeaderTextProps = {\n header: string;\n /** Underlines the label: the header has a tooltip. */\n explained: boolean;\n};\n\n/** The header's label, as a sort button with the sorted direction's arrow. Takes a tooltip's trigger props. */\nfunction SortButton({\n header,\n explained,\n sorted,\n className,\n ...props\n}: HeaderTextProps & { sorted?: MetricsTableSortDirection } & ComponentProps<'button'>) {\n const SortIcon = sorted && SORT_ICON[sorted];\n return (\n <button\n type=\"button\"\n {...props}\n className={cn(\n 'group/header flex items-center gap-1 rounded whitespace-nowrap hover:text-muted-foreground',\n FOCUS,\n className,\n )}\n >\n <span className={cn(explained && EXPLAINED)}>{header}</span>\n {SortIcon && <SortIcon aria-hidden className=\"size-3\" />}\n </button>\n );\n}\n\n/** The header's label as text; focusable when it has a tooltip, so the keyboard reaches it. */\nfunction HeaderText({ header, explained, className, ...props }: HeaderTextProps & ComponentProps<'span'>) {\n return (\n <span\n tabIndex={explained ? 0 : undefined}\n {...props}\n className={cn('group/header rounded whitespace-nowrap', FOCUS, className)}\n >\n <span className={cn(explained && EXPLAINED)}>{header}</span>\n </span>\n );\n}\n\n/**\n * A column's header: a sort button when the column sorts, plain text otherwise. With a\n * `description`, the label itself shows it in a tooltip (no separate info icon), marked by a\n * dotted underline, and screen readers get it as the header's description.\n */\nexport function MetricsTableHeaderLabel({\n header,\n description,\n sorted,\n onSort,\n}: {\n header: string;\n description?: ReactNode;\n sorted?: MetricsTableSortDirection;\n /** Makes the header a sort button. */\n onSort?: () => void;\n}) {\n const descriptionId = useId();\n const explained = Boolean(description);\n const describedBy = explained ? descriptionId : undefined;\n const trigger = onSort ? (\n <SortButton header={header} explained={explained} sorted={sorted} onClick={onSort} aria-describedby={describedBy} />\n ) : (\n <HeaderText header={header} explained={explained} aria-describedby={describedBy} />\n );\n\n if (!explained) return trigger;\n return (\n <Tooltip>\n <TooltipTrigger render={trigger} />\n {/* Kept mounted (hidden) while closed, so screen readers always get the text. */}\n <TooltipContent id={descriptionId} keepMounted side=\"top\" className=\"max-w-72\">\n {description}\n </TooltipContent>\n </Tooltip>\n );\n}\n","import type { KeyboardEvent, MouseEvent } from 'react';\nimport { FOCUS } from './metrics-table-header';\nimport type { MetricsTableColumn, MetricsTableProps } from './metrics-table-types';\nimport { Skeleton } from '@/ds/components/Skeleton';\nimport { cn } from '@/lib/utils';\n\n/**\n * Full-width lines that fade over their last 6px. A hovered row covers the lines above and below\n * it, which then fade over 24px so they read as part of the hover. Written out in full so Tailwind\n * finds every class.\n */\nconst ROW_LINE =\n 'isolate before:absolute before:inset-x-0 before:-top-px before:bottom-0 before:-z-10 before:rounded hover:before:bg-fill-subtle after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border last:after:hidden after:[mask-image:linear-gradient(to_right,transparent,black_6px,black_calc(100%-6px),transparent)] hover:after:[mask-image:linear-gradient(to_right,transparent,black_24px,black_calc(100%-24px),transparent)] [&:has(+:hover)]:after:[mask-image:linear-gradient(to_right,transparent,black_24px,black_calc(100%-24px),transparent)]';\n\n/**\n * Rows are 34.5px: 7px above 13px text (a 19.5px line) and 8px below, one more pixel for the\n * separator drawn in the row's last pixel, so text sits centered between the lines.\n */\nconst ROW_BOX = 'pt-1.75 pb-2 text-body-sm';\n\nconst ROW = cn('relative col-span-full grid grid-cols-subgrid items-center px-3', ROW_BOX, ROW_LINE);\n\n/** Skeleton widths for the first column's names, so loading rows don't look stamped. */\nconst SKELETON_NAMES = ['w-32', 'w-24', 'w-36', 'w-20', 'w-28'];\n\n/** Controls a cell can hold; their own clicks and keys don't count as the row's. */\nconst CONTROLS = 'a, button, input, select, textarea, label, [role=\"button\"], [role=\"link\"], [contenteditable]';\n\n/** Whether an event started on a control inside the row rather than on the row itself. */\nfunction fromControl(event: MouseEvent | KeyboardEvent) {\n if (!(event.target instanceof Element)) return false;\n const control = event.target.closest(CONTROLS);\n return Boolean(control && event.currentTarget.contains(control));\n}\n\n/** Where a loading cell's bar sits and how wide it is: figures right, the name in varied widths. */\nfunction skeletonBar<Row>(column: MetricsTableColumn<Row>, columnIndex: number, rowIndex: number) {\n if (column.align === 'end') return 'right-0 w-10';\n if (columnIndex > 0) return 'left-0 w-16';\n return cn('left-0', SKELETON_NAMES[rowIndex % SKELETON_NAMES.length]);\n}\n\ntype RowBehavior<Row> = Pick<\n MetricsTableProps<Row>,\n 'onRowClick' | 'onRowHover' | 'isRowDimmed' | 'isRowActive' | 'rowTitle'\n>;\n\nexport type MetricsTableBodyProps<Row> = RowBehavior<Row> & {\n columns: MetricsTableColumn<Row>[];\n /** The rows on the current page, sorted. */\n rows: Row[];\n getRowKey: (row: Row) => string;\n empty: MetricsTableProps<Row>['empty'];\n /** A paged table's rows per page: drawn while loading, and kept as the empty table's height. */\n pageSize: number | undefined;\n isLoading: boolean;\n};\n\n/** Skeleton rows while loading, the empty message without rows, else the page's rows. */\nexport function MetricsTableBody<Row>({\n columns,\n rows,\n getRowKey,\n empty,\n pageSize,\n isLoading,\n ...behavior\n}: MetricsTableBodyProps<Row>) {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 5 }, (_, i) => (\n <MetricsTableSkeletonRow key={i} columns={columns} rowIndex={i} />\n ));\n }\n if (rows.length === 0) {\n if (pageSize) {\n if (empty === undefined) return <MetricsTableFiller count={pageSize} />;\n // Empty, a paged table keeps at least a full page's height, with the message in its middle.\n // Both share one grid cell, so a message taller than the page still grows the row.\n return (\n <div role=\"row\" className=\"col-span-full grid *:col-start-1 *:row-start-1\">\n <MetricsTableFiller count={pageSize} />\n <p\n role=\"cell\"\n className=\"flex items-center justify-center px-3 py-4 text-center text-body-sm text-muted-foreground\"\n >\n {empty}\n </p>\n </div>\n );\n }\n if (empty === undefined) return null;\n return (\n <div role=\"row\" className=\"col-span-full\">\n <p role=\"cell\" className=\"py-8 text-center text-body-sm text-muted-foreground\">\n {empty}\n </p>\n </div>\n );\n }\n return rows.map(row => <MetricsTableRow key={getRowKey(row)} row={row} columns={columns} {...behavior} />);\n}\n\n/**\n * Blank space as tall as `count` rows, so a paged table is a full page tall however many rows it\n * shows. Rows of the same box, not a fixed height, so it follows the row's text size.\n */\nexport function MetricsTableFiller({ count }: { count: number }) {\n if (count <= 0) return null;\n return (\n <div aria-hidden data-slot=\"metrics-table-filler\" className=\"col-span-full\">\n {Array.from({ length: count }, (_, i) => (\n <div key={i} className={ROW_BOX}>\n {'\\u200b'}\n </div>\n ))}\n </div>\n );\n}\n\n/** A loading row: the real row's markup with a bar in each cell, so it has the same height. */\nfunction MetricsTableSkeletonRow<Row>({ columns, rowIndex }: { columns: MetricsTableColumn<Row>[]; rowIndex: number }) {\n return (\n <div role=\"row\" aria-busy className={ROW}>\n {columns.map((column, columnIndex) => (\n <div\n key={column.id}\n role=\"cell\"\n className={cn('relative flex min-w-0', column.align === 'end' && 'justify-end')}\n >\n {/* A zero-width space gives the cell a real line box, so loading rows match loaded ones. */}\n {''}\n <Skeleton\n className={cn('absolute top-1/2 h-3.5 -translate-y-1/2', skeletonBar(column, columnIndex, rowIndex))}\n />\n </div>\n ))}\n </div>\n );\n}\n\n/** One row: its cells, and with `onRowClick` a focusable row that Enter and Space also click. */\nfunction MetricsTableRow<Row>({\n row,\n columns,\n onRowClick,\n onRowHover,\n isRowDimmed,\n isRowActive,\n rowTitle,\n}: RowBehavior<Row> & { row: Row; columns: MetricsTableColumn<Row>[] }) {\n const active = isRowActive?.(row) ?? false;\n const dimmed = isRowDimmed?.(row) ?? false;\n\n const handleClick = (event: MouseEvent) => {\n if (!fromControl(event)) onRowClick?.(row);\n };\n const handleKeyDown = (event: KeyboardEvent) => {\n // Only while the row itself has focus, so a cell's link or input keeps its keys.\n if (event.target !== event.currentTarget) return;\n if (event.key !== 'Enter' && event.key !== ' ') return;\n // Space would also scroll the page.\n event.preventDefault();\n onRowClick?.(row);\n };\n\n return (\n <div\n role=\"row\"\n aria-current={active || undefined}\n tabIndex={onRowClick ? 0 : undefined}\n title={rowTitle?.(row)}\n onClick={onRowClick ? handleClick : undefined}\n onKeyDown={onRowClick ? handleKeyDown : undefined}\n onMouseEnter={onRowHover ? () => onRowHover(row) : undefined}\n onMouseLeave={onRowHover ? () => onRowHover(undefined) : undefined}\n className={cn(\n ROW,\n 'rounded text-foreground transition-opacity duration-150',\n onRowClick && cn('cursor-pointer', FOCUS),\n active && 'before:bg-fill-subtle',\n dimmed && 'opacity-40',\n )}\n >\n {columns.map(column => (\n <div\n key={column.id}\n role=\"cell\"\n className={cn('flex min-w-0 items-center', column.align === 'end' && 'justify-end text-right')}\n >\n {column.cell(row)}\n </div>\n ))}\n </div>\n );\n}\n","import { useState } from 'react';\nimport { MetricsTableHeaderLabel } from './metrics-table-header';\nimport { MetricsTableBody, MetricsTableFiller } from './metrics-table-rows';\nimport type { MetricsTableColumn, MetricsTableProps, MetricsTableSort } from './metrics-table-types';\nimport { ScrollArea } from '@/ds/components/ScrollArea/scroll-area';\nimport { TablePagination } from '@/ds/components/Table/table-pagination';\nimport { cn } from '@/lib/utils';\n\n/**\n * A full-width line that fades over its last 6px; over 24px when the first row is hovered, so it\n * reads as part of the hover. Written out in full so Tailwind finds every class.\n */\nconst HEADER_LINE =\n 'after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border after:[mask-image:linear-gradient(to_right,transparent,black_6px,black_calc(100%-6px),transparent)] [&:has(+:hover)]:after:[mask-image:linear-gradient(to_right,transparent,black_24px,black_calc(100%-24px),transparent)]';\n\nconst ARIA_SORT = { asc: 'ascending', desc: 'descending' } as const;\n\n/**\n * Clicks on a column cycle its first direction, the other one, then off: off goes back to the\n * table's default order. Another column starts at its own first direction.\n */\nfunction nextSort<Row>(\n current: MetricsTableSort | undefined,\n column: MetricsTableColumn<Row>,\n): MetricsTableSort | undefined {\n const first = column.firstDirection ?? 'desc';\n if (current?.column !== column.id) return { column: column.id, direction: first };\n if (current.direction !== first) return undefined;\n return { column: column.id, direction: first === 'desc' ? 'asc' : 'desc' };\n}\n\n/** Ascending order of two present sort values. */\nfunction compareAscending(x: number | string, y: number | string) {\n if (typeof x === 'number' && typeof y === 'number') return x - y;\n return String(x).localeCompare(String(y));\n}\n\nfunction sortRows<Row>(rows: Row[], columns: MetricsTableColumn<Row>[], sort: MetricsTableSort | undefined) {\n const by = columns.find(c => c.id === sort?.column)?.sortValue;\n if (!sort || !by) return rows;\n const sign = sort.direction === 'desc' ? -1 : 1;\n // A row without a value goes last whichever way the column sorts.\n return rows.toSorted((a, b) => {\n const x = by(a);\n const y = by(b);\n if (x === undefined && y === undefined) return 0;\n if (x === undefined) return 1;\n if (y === undefined) return -1;\n return sign * compareAscending(x, y);\n });\n}\n\n/**\n * A column's grid track. Figures take a share of the spare width, like an HTML table's auto\n * layout: never narrower than their values, wider when the card has room.\n */\nconst columnWidth = <Row,>(column: MetricsTableColumn<Row>) =>\n column.width ?? (column.align === 'end' ? 'minmax(max-content,1fr)' : 'max-content');\n\n/**\n * A table for the numbers behind a chart: one row per series or group, columns for its figures,\n * separated by lines that fade at the ends. With `SeriesMark` and `Sparkline` cells and the row\n * hover and click hooks it doubles as the chart's legend; without them it's a plain data table.\n * With `pageSize` it pages long lists, and is always a full page tall. For a share of a total, a compact `ShareBar` fits better.\n */\nexport function MetricsTable<Row>({\n columns,\n rows,\n getRowKey,\n sort,\n defaultSort,\n onSortChange,\n onRowClick,\n onRowHover,\n isRowDimmed,\n isRowActive,\n rowTitle,\n empty,\n pageSize,\n isLoading = false,\n 'aria-label': ariaLabel,\n className,\n}: MetricsTableProps<Row>) {\n const [ownSort, setOwnSort] = useState(defaultSort);\n const [page, setPage] = useState(0);\n const current = sort ?? ownSort;\n const sorted = sortRows(rows, columns, current);\n const pageCount = pageSize ? Math.ceil(sorted.length / pageSize) : 1;\n // Fewer rows than before (a narrower filter) keeps the last page rather than an empty one.\n const safePage = Math.min(page, Math.max(0, pageCount - 1));\n const shown = pageSize ? sorted.slice(safePage * pageSize, (safePage + 1) * pageSize) : sorted;\n const template = { gridTemplateColumns: columns.map(columnWidth).join(' ') };\n const showsPagination = !isLoading && pageCount > 1;\n\n const sortBy = (column: MetricsTableColumn<Row>) => {\n const next = nextSort(current, column) ?? defaultSort;\n setOwnSort(next);\n setPage(0);\n onSortChange?.(next);\n };\n\n return (\n <div className={cn('grid min-w-0 gap-3', className)}>\n {/* Narrower than its content (a phone), the table scrolls sideways instead of squeezing columns or the page. */}\n <ScrollArea orientation=\"horizontal\">\n {/* One set of column tracks for the header and every row (each row is a subgrid), so a column's\n width follows its widest cell in the whole table and headers sit over their cells. */}\n <div\n role=\"table\"\n aria-label={ariaLabel}\n // min-content, not max-content: a long name would otherwise grow every `fr` column with it.\n className=\"grid min-w-min gap-x-4\"\n style={template}\n >\n <div\n role=\"row\"\n className={cn(\n 'relative col-span-full grid grid-cols-subgrid items-end px-3 pb-1.5 text-body-sm font-medium text-placeholder',\n HEADER_LINE,\n )}\n >\n {columns.map(column => {\n const direction = current?.column === column.id ? current.direction : undefined;\n return (\n <div\n key={column.id}\n role=\"columnheader\"\n aria-sort={direction && ARIA_SORT[direction]}\n className={cn('flex min-w-0 items-center gap-1', column.align === 'end' && 'justify-end text-right')}\n >\n <MetricsTableHeaderLabel\n header={column.header}\n description={column.description}\n sorted={direction}\n onSort={column.sortValue ? () => sortBy(column) : undefined}\n />\n </div>\n );\n })}\n </div>\n <MetricsTableBody\n columns={columns}\n rows={shown}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n onRowHover={onRowHover}\n isRowDimmed={isRowDimmed}\n isRowActive={isRowActive}\n rowTitle={rowTitle}\n empty={empty}\n pageSize={pageSize}\n isLoading={isLoading}\n />\n </div>\n {/* Always a full page tall, so loading, paging and switching data don't move what's below. */}\n {pageSize !== undefined && !isLoading && shown.length > 0 && (\n <MetricsTableFiller count={pageSize - shown.length} />\n )}\n </ScrollArea>\n {showsPagination && <TablePagination page={safePage} pageCount={pageCount} onPageChange={setPage} />}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -202,7 +202,7 @@ function AskUserTextAnswer({ disabled = false, ...props }) {
|
|
|
202
202
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_input.Input, {
|
|
203
203
|
type: "text",
|
|
204
204
|
"aria-labelledby": props["aria-label"] ? void 0 : context.questionId,
|
|
205
|
-
placeholder: "Type your answer
|
|
205
|
+
placeholder: "Type your answer",
|
|
206
206
|
size: "sm",
|
|
207
207
|
...props,
|
|
208
208
|
disabled: context.disabled || disabled,
|
|
@@ -492,4 +492,4 @@ Object.defineProperty(exports, "AskUserTextAnswer", {
|
|
|
492
492
|
}
|
|
493
493
|
});
|
|
494
494
|
|
|
495
|
-
//# sourceMappingURL=ask-user-
|
|
495
|
+
//# sourceMappingURL=ask-user-C1p2PEYe.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-user-C1p2PEYe.cjs","names":["createContext","useContext","cn","raisedSurfaceStyle","Icon","MessageCircleQuestion","Txt","FieldItem","FieldLabel","Button","Check","Badge","Fieldset","cn","RadioGroup","Checkbox","RadioGroupItem","Input","FieldItem","FieldLabel","Txt","useId","useState","_Fragment","Badge"],"sources":["../src/ds/components/ai/ask-user/ask-user-context.ts","../src/ds/components/ai/ask-user/ask-user-layout.tsx","../src/ds/components/ai/ask-user/ask-user-controls.tsx","../src/ds/components/ai/ask-user/ask-user-root.tsx","../src/ds/components/ai/ask-user/ask-user-card.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\nimport type { AskUserSelectionMode } from './ask-user-types';\n\ninterface AskUserContextValue {\n questionId: string;\n selectionMode: AskUserSelectionMode;\n disabled: boolean;\n answerText: string;\n selectedOptionLabels: string[];\n isCustomAnswerSelected: boolean;\n canSubmitAnswer: boolean;\n selectOption: (optionLabel: string) => void;\n setCustomAnswerSelected: (selected: boolean) => void;\n changeAnswerText: (text: string) => void;\n unselectEmptyCustomAnswer: () => void;\n submitAnswer: () => void;\n}\n\nexport const AskUserContext = createContext<AskUserContextValue | undefined>(undefined);\n\nexport function useOptionalAskUserContext() {\n return useContext(AskUserContext);\n}\n\nexport function useAskUserContext() {\n const context = useOptionalAskUserContext();\n if (!context) throw new Error('AskUser controls must be rendered inside AskUser.Root.');\n return context;\n}\n","import { Check, MessageCircleQuestion } from 'lucide-react';\nimport type { ComponentProps, ReactNode } from 'react';\nimport { useOptionalAskUserContext } from './ask-user-context';\nimport type { AskUserResult } from './ask-user-types';\nimport { Badge } from '@/ds/components/Badge';\nimport { Button } from '@/ds/components/Button';\nimport { FieldItem, FieldLabel } from '@/ds/components/Field';\nimport type { FieldLabelProps } from '@/ds/components/Field';\nimport { Txt } from '@/ds/components/Txt';\nimport { Icon } from '@/ds/icons/Icon';\nimport { raisedSurfaceStyle } from '@/ds/primitives/raised-surface';\nimport { cn } from '@/lib/utils';\n\nexport const AskUserContainer = ({ className, ...props }: ComponentProps<'div'>) => (\n <div\n data-slot=\"ask-user\"\n className={cn(raisedSurfaceStyle, 'w-full overflow-hidden rounded-xl', className)}\n {...props}\n />\n);\n\nexport const AskUserLabel = ({ children = 'Question', className, ...props }: ComponentProps<'div'>) => (\n <div data-slot=\"ask-user-label\" className={cn('flex min-h-10 items-center gap-2 px-4 pt-3', className)} {...props}>\n <Icon size=\"xs\" className=\"text-muted-foreground\">\n <MessageCircleQuestion />\n </Icon>\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\">\n {children}\n </Txt>\n </div>\n);\n\nexport const AskUserBody = ({ className, ...props }: ComponentProps<'div'>) => (\n <div data-slot=\"ask-user-body\" className={cn('p-4', className)} {...props} />\n);\n\nconst AskUserQuestionTitle = ({ children }: { children: ReactNode }) => (\n <span className=\"flex items-start gap-2\">\n <Icon size=\"xs\" className={cn('mt-1 shrink-0', 'text-muted-foreground')} aria-hidden>\n <MessageCircleQuestion />\n </Icon>\n <span className=\"min-w-0\">{children}</span>\n </span>\n);\n\nexport const AskUserQuestion = ({ children, className, ...props }: ComponentProps<typeof Txt>) => {\n const context = useOptionalAskUserContext();\n return (\n <Txt\n as=\"p\"\n variant=\"subheading\"\n tone=\"ink\"\n {...props}\n id={context?.questionId ?? props.id}\n className={cn('mb-3', className)}\n >\n <AskUserQuestionTitle>{children}</AskUserQuestionTitle>\n </Txt>\n );\n};\n\nexport interface AskUserOptionRowProps extends Omit<FieldLabelProps, 'children'> {\n control: ReactNode;\n label: ReactNode;\n description?: string;\n disabled?: boolean;\n}\n\nexport const AskUserOptionRow = ({\n control,\n label,\n description,\n disabled = false,\n className,\n ...props\n}: AskUserOptionRowProps) => (\n <FieldItem disabled={disabled} className=\"contents\">\n <FieldLabel\n // state-layer's wash only stops at :disabled/aria-disabled, and a <label> is neither\n aria-disabled={disabled || undefined}\n className={cn('state-layer flex items-start gap-2.5 rounded-lg px-3 py-2 data-disabled:opacity-50', className)}\n {...props}\n >\n {control}\n <span className=\"grid gap-0.5\">\n <Txt as=\"span\" variant=\"body\" tone=\"ink\">\n {label}\n </Txt>\n {description ? (\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\">\n {description}\n </Txt>\n ) : null}\n </span>\n </FieldLabel>\n </FieldItem>\n);\n\nexport type AskUserSubmitButtonProps = Omit<ComponentProps<typeof Button>, 'children'> & { children?: ReactNode };\n\nexport const AskUserSubmitButton = ({ children = 'Submit answer', ...props }: AskUserSubmitButtonProps) => (\n <Button icon={<Check />} type=\"button\" size=\"sm\" variant=\"primary\" {...props}>\n {children}\n </Button>\n);\n\nexport const AskUserPending = ({ children = 'Submitting…', ...props }: ComponentProps<typeof Txt>) => (\n <Txt as=\"span\" role=\"status\" variant=\"caption\" tone=\"muted\" {...props}>\n {children}\n </Txt>\n);\n\nexport interface AskUserOutputProps extends ComponentProps<'div'> {\n result: AskUserResult;\n}\n\nexport const AskUserOutput = ({ result, className, ...props }: AskUserOutputProps) => (\n <div\n data-slot=\"ask-user-output\"\n role={result.isError ? 'alert' : 'status'}\n className={cn('grid gap-2 rounded-lg bg-fill p-3', className)}\n {...props}\n >\n <Badge size=\"xs\" variant={result.isError ? 'destructive' : 'success'} className=\"justify-self-start\">\n {result.isError ? 'Error' : 'Answered'}\n </Badge>\n <Txt as=\"p\" variant=\"body\" tone=\"ink\" className={cn(result.isError && 'text-destructive-foreground')}>\n {result.content}\n </Txt>\n </div>\n);\n","import type { ChangeEvent, ComponentProps, KeyboardEvent, MouseEvent, ReactNode } from 'react';\nimport { useAskUserContext, useOptionalAskUserContext } from './ask-user-context';\nimport { AskUserOptionRow, AskUserSubmitButton } from './ask-user-layout';\nimport { Checkbox } from '@/ds/components/Checkbox';\nimport { FieldItem, FieldLabel, Fieldset } from '@/ds/components/Field';\nimport { Input } from '@/ds/components/Input';\nimport { RadioGroup, RadioGroupItem } from '@/ds/components/RadioGroup';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\n// Keep the UI-only custom choice separate from suggested answer values.\nconst customAnswerValue = 'custom';\nconst optionValuePrefix = 'option:';\n\nfunction getOptionRadioValue(optionLabel: string): string {\n return `${optionValuePrefix}${optionLabel}`;\n}\n\nexport function AskUserOptions({ children, className, ...props }: ComponentProps<typeof Fieldset>) {\n const context = useAskUserContext();\n\n const handleSingleSelectChange = (radioValue: unknown) => {\n if (radioValue === customAnswerValue) {\n context.setCustomAnswerSelected(true);\n return;\n }\n if (typeof radioValue !== 'string' || !radioValue.startsWith(optionValuePrefix)) return;\n context.selectOption(radioValue.slice(optionValuePrefix.length));\n };\n\n if (context.selectionMode === 'multi_select') {\n return (\n <Fieldset aria-label=\"Answer options\" {...props} className={cn('gap-2', className)}>\n {children}\n </Fieldset>\n );\n }\n\n const selectedOptionLabel = context.selectedOptionLabels[0];\n const selectedOptionValue = selectedOptionLabel === undefined ? '' : getOptionRadioValue(selectedOptionLabel);\n const selectedRadioValue = context.isCustomAnswerSelected ? customAnswerValue : selectedOptionValue;\n\n return (\n <Fieldset\n {...props}\n className={cn('gap-2', className)}\n render={\n <RadioGroup\n aria-labelledby={context.questionId}\n disabled={context.disabled}\n value={selectedRadioValue}\n onValueChange={handleSingleSelectChange}\n />\n }\n >\n {children}\n </Fieldset>\n );\n}\n\nexport interface AskUserOptionItemProps {\n value: string;\n children: ReactNode;\n description?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function AskUserOptionItem({\n value,\n children,\n description,\n disabled = false,\n className,\n}: AskUserOptionItemProps) {\n const context = useAskUserContext();\n const isDisabled = context.disabled || disabled;\n\n const handleOptionToggle = () => {\n if (!isDisabled) context.selectOption(value);\n };\n\n return (\n <AskUserOptionRow\n label={children}\n description={description}\n disabled={isDisabled}\n className={className}\n control={\n context.selectionMode === 'multi_select' ? (\n <Checkbox\n className=\"mt-0.5\"\n disabled={isDisabled}\n checked={context.selectedOptionLabels.includes(value)}\n onCheckedChange={handleOptionToggle}\n />\n ) : (\n <RadioGroupItem className=\"mt-0.5\" disabled={isDisabled} value={getOptionRadioValue(value)} />\n )\n }\n />\n );\n}\n\nexport type AskUserTextAnswerProps = Omit<\n ComponentProps<typeof Input>,\n 'value' | 'defaultValue' | 'onChange' | 'onKeyDown'\n>;\n\nexport function AskUserTextAnswer({ disabled = false, ...props }: AskUserTextAnswerProps) {\n const context = useAskUserContext();\n\n const handleAnswerTextChange = (event: ChangeEvent<HTMLInputElement>) => {\n context.changeAnswerText(event.target.value);\n };\n\n const handleAnswerTextKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter' && !event.nativeEvent.isComposing) {\n event.preventDefault();\n context.submitAnswer();\n }\n };\n\n return (\n <Input\n type=\"text\"\n aria-labelledby={props['aria-label'] ? undefined : context.questionId}\n placeholder=\"Type your answer\"\n size=\"sm\"\n {...props}\n disabled={context.disabled || disabled}\n value={context.answerText}\n onChange={handleAnswerTextChange}\n onKeyDown={handleAnswerTextKeyDown}\n />\n );\n}\n\nexport interface AskUserCustomAnswerProps {\n className?: string;\n label?: string;\n inputLabel?: string;\n}\n\nexport function AskUserCustomAnswer({\n className,\n label = 'Other…',\n inputLabel = 'Your answer',\n}: AskUserCustomAnswerProps) {\n const context = useAskUserContext();\n\n const handleRowClick = (event: MouseEvent<HTMLDivElement>) => {\n if (context.disabled || event.target !== event.currentTarget) return;\n if (context.isCustomAnswerSelected) {\n event.currentTarget.querySelector<HTMLInputElement>('input[type=\"text\"]')?.focus();\n return;\n }\n event.currentTarget.querySelector<HTMLElement>('[role=\"radio\"], [role=\"checkbox\"]')?.click();\n };\n\n const handleControlMouseDown = (event: MouseEvent<HTMLSpanElement>) => {\n // Let the checkbox click unselect it before blur resets its controlled value.\n if (context.isCustomAnswerSelected && !context.answerText.trim()) event.preventDefault();\n };\n\n return (\n <FieldItem\n role=\"group\"\n aria-label=\"Custom answer\"\n aria-disabled={context.disabled || undefined}\n disabled={context.disabled}\n onClick={handleRowClick}\n className={cn(\n 'items-start gap-2.5 rounded-lg px-3 py-2 data-disabled:opacity-50',\n !context.isCustomAnswerSelected && 'state-layer',\n className,\n )}\n >\n {context.selectionMode === 'multi_select' ? (\n <Checkbox\n className=\"mt-0.5\"\n aria-label={label}\n disabled={context.disabled}\n checked={context.isCustomAnswerSelected}\n onMouseDown={handleControlMouseDown}\n onCheckedChange={context.setCustomAnswerSelected}\n />\n ) : (\n <RadioGroupItem aria-label={label} className=\"mt-0.5\" value={customAnswerValue} />\n )}\n {context.isCustomAnswerSelected ? (\n <AskUserTextAnswer\n aria-label={inputLabel}\n onBlur={context.unselectEmptyCustomAnswer}\n placeholder={label}\n variant=\"unstyled\"\n className={cn('h-auto min-w-0 flex-1 px-0', 'text-body')}\n autoFocus\n />\n ) : (\n <FieldLabel className=\"flex-1\">\n <Txt as=\"span\" variant=\"body\" tone=\"ink\">\n {label}\n </Txt>\n </FieldLabel>\n )}\n </FieldItem>\n );\n}\n\nexport type AskUserSubmitProps = ComponentProps<typeof AskUserSubmitButton> & {\n when?: 'always' | 'custom-answer';\n};\n\nexport function AskUserSubmit({ when = 'always', disabled = false, onClick, ...props }: AskUserSubmitProps) {\n const context = useOptionalAskUserContext();\n\n const handleSubmitClick = (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (!event.defaultPrevented && !disabled) context?.submitAnswer();\n };\n\n if (when === 'custom-answer' && !context?.isCustomAnswerSelected) return null;\n\n return (\n <AskUserSubmitButton\n {...props}\n disabled={disabled || (context !== undefined && !context.canSubmitAnswer)}\n onClick={handleSubmitClick}\n />\n );\n}\n","import { useId, useState } from 'react';\nimport type { ComponentProps } from 'react';\nimport { AskUserContext } from './ask-user-context';\nimport { AskUserContainer } from './ask-user-layout';\nimport type { AskUserAnswer, AskUserSelectionMode } from './ask-user-types';\n\nexport interface AskUserRootProps extends Omit<ComponentProps<typeof AskUserContainer>, 'onSubmit'> {\n selectionMode?: AskUserSelectionMode;\n disabled?: boolean;\n onSubmit: (answer: AskUserAnswer) => void;\n}\n\ninterface AskUserAnswerDraft {\n answerText: string;\n selectedOptionLabels: string[];\n selectionMode: AskUserSelectionMode;\n isCustomAnswerSelected: boolean;\n}\n\nfunction getAnswerToSubmit({\n answerText,\n selectedOptionLabels,\n selectionMode,\n isCustomAnswerSelected,\n}: AskUserAnswerDraft): AskUserAnswer | undefined {\n const trimmedAnswerText = answerText.trim();\n if (isCustomAnswerSelected && !trimmedAnswerText) return undefined;\n if (selectionMode === 'single_select') {\n // Suggested single-select answers already submit when selected.\n if (!isCustomAnswerSelected && selectedOptionLabels.length > 0) return undefined;\n return trimmedAnswerText || undefined;\n }\n\n const answerLabels = isCustomAnswerSelected ? [...selectedOptionLabels, trimmedAnswerText] : selectedOptionLabels;\n return answerLabels.length > 0 ? answerLabels : undefined;\n}\n\nfunction toggleSelectedOptionLabel(selectedOptionLabels: string[], optionLabel: string): string[] {\n if (selectedOptionLabels.includes(optionLabel)) {\n return selectedOptionLabels.filter(selectedLabel => selectedLabel !== optionLabel);\n }\n return [...selectedOptionLabels, optionLabel];\n}\n\nexport function AskUserRoot({\n selectionMode = 'single_select',\n disabled = false,\n onSubmit,\n children,\n ...props\n}: AskUserRootProps) {\n const questionId = useId();\n const [answerText, setAnswerText] = useState('');\n const [selectedOptionLabels, setSelectedOptionLabels] = useState<string[]>([]);\n const [isCustomAnswerSelected, setIsCustomAnswerSelected] = useState(false);\n const answerToSubmit = getAnswerToSubmit({\n answerText,\n selectedOptionLabels,\n selectionMode,\n isCustomAnswerSelected,\n });\n const canSubmitAnswer = !disabled && answerToSubmit !== undefined;\n\n const selectOption = (optionLabel: string) => {\n if (disabled) return;\n if (selectionMode === 'multi_select') {\n setSelectedOptionLabels(currentLabels => toggleSelectedOptionLabel(currentLabels, optionLabel));\n return;\n }\n setIsCustomAnswerSelected(false);\n setSelectedOptionLabels([optionLabel]);\n onSubmit(optionLabel);\n };\n\n const setCustomAnswerSelected = (selected: boolean) => {\n if (disabled) return;\n setIsCustomAnswerSelected(selected);\n if (selected && selectionMode === 'single_select') setSelectedOptionLabels([]);\n };\n\n const changeAnswerText = (text: string) => {\n if (!disabled) setAnswerText(text);\n };\n\n const unselectEmptyCustomAnswer = () => {\n if (!disabled && !answerText.trim()) setIsCustomAnswerSelected(false);\n };\n\n const submitAnswer = () => {\n if (disabled || answerToSubmit === undefined) return;\n onSubmit(answerToSubmit);\n };\n\n return (\n <AskUserContext.Provider\n value={{\n questionId,\n selectionMode,\n disabled,\n answerText,\n selectedOptionLabels,\n isCustomAnswerSelected,\n canSubmitAnswer,\n selectOption,\n setCustomAnswerSelected,\n changeAnswerText,\n unselectEmptyCustomAnswer,\n submitAnswer,\n }}\n >\n <AskUserContainer role=\"group\" aria-labelledby={questionId} {...props}>\n {children}\n </AskUserContainer>\n </AskUserContext.Provider>\n );\n}\n","import type { ComponentProps, ReactNode } from 'react';\nimport { useAskUserContext } from './ask-user-context';\nimport {\n AskUserCustomAnswer,\n AskUserOptionItem,\n AskUserOptions,\n AskUserSubmit,\n AskUserTextAnswer,\n} from './ask-user-controls';\nimport type { AskUserContainer } from './ask-user-layout';\nimport { AskUserBody, AskUserOutput, AskUserPending, AskUserQuestion } from './ask-user-layout';\nimport { AskUserRoot } from './ask-user-root';\nimport type { AskUserAnswer, AskUserOption, AskUserPayload, AskUserResult } from './ask-user-types';\nimport { Badge } from '@/ds/components/Badge';\n\nexport interface AskUserProps extends Omit<ComponentProps<typeof AskUserContainer>, 'children' | 'onSubmit'> {\n payload: AskUserPayload;\n result?: AskUserResult;\n isAnswered?: boolean;\n isSubmitting?: boolean;\n onSubmit: (answer: AskUserAnswer) => void;\n footer?: ReactNode;\n}\n\nfunction getValidOptions(options: AskUserPayload['options']): AskUserOption[] {\n return (\n options?.filter((option): option is AskUserOption =>\n Boolean(option && typeof option.label === 'string' && option.label),\n ) ?? []\n );\n}\n\nfunction AskUserAnswerControls({ options }: { options: AskUserOption[] }) {\n const { selectionMode } = useAskUserContext();\n const isMultiSelect = selectionMode === 'multi_select';\n if (options.length === 0) {\n return (\n <div className=\"flex items-center gap-2\">\n <AskUserTextAnswer />\n <AskUserSubmit className=\"shrink-0 whitespace-nowrap\" />\n </div>\n );\n }\n\n return (\n <>\n <AskUserOptions>\n {options.map(option => (\n <AskUserOptionItem key={option.label} value={option.label} description={option.description}>\n {option.label}\n </AskUserOptionItem>\n ))}\n <AskUserCustomAnswer />\n {isMultiSelect ? <AskUserSubmit className=\"mt-1 justify-self-start\" /> : null}\n </AskUserOptions>\n {!isMultiSelect ? <AskUserSubmit when=\"custom-answer\" className=\"mt-2\" /> : null}\n </>\n );\n}\n\ninterface AskUserCardContentProps extends Pick<AskUserProps, 'result' | 'isAnswered' | 'isSubmitting' | 'footer'> {\n options: AskUserOption[];\n}\n\nfunction AskUserCardContent({ result, isAnswered, isSubmitting, options, footer }: AskUserCardContentProps) {\n if (result) return <AskUserOutput result={result} />;\n if (isAnswered) return <Badge variant=\"success\">Answered</Badge>;\n\n return (\n <>\n <AskUserAnswerControls options={options} />\n {isSubmitting ? <AskUserPending className=\"mt-3 block\" /> : null}\n {footer}\n </>\n );\n}\n\nexport function AskUser({\n payload,\n result,\n isAnswered = false,\n isSubmitting = false,\n onSubmit,\n footer,\n ...props\n}: AskUserProps) {\n const options = getValidOptions(payload.options);\n const isMultiSelect = options.length > 0 && payload.selectionMode === 'multi_select';\n const payloadKey = JSON.stringify([payload.question, options.map(option => option.label), payload.selectionMode]);\n const hasAnswer = result !== undefined || isAnswered;\n\n return (\n <AskUserRoot\n key={payloadKey}\n data-testid=\"ask-user\"\n selectionMode={isMultiSelect ? 'multi_select' : 'single_select'}\n disabled={isSubmitting || hasAnswer}\n onSubmit={onSubmit}\n {...props}\n >\n <AskUserBody>\n <AskUserQuestion>{payload.question}</AskUserQuestion>\n <AskUserCardContent\n result={result}\n isAnswered={isAnswered}\n isSubmitting={isSubmitting}\n options={options}\n footer={footer}\n />\n </AskUserBody>\n </AskUserRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -202,7 +202,7 @@ function AskUserTextAnswer({ disabled = false, ...props }) {
|
|
|
202
202
|
return /* @__PURE__ */ jsx(Input, {
|
|
203
203
|
type: "text",
|
|
204
204
|
"aria-labelledby": props["aria-label"] ? void 0 : context.questionId,
|
|
205
|
-
placeholder: "Type your answer
|
|
205
|
+
placeholder: "Type your answer",
|
|
206
206
|
size: "sm",
|
|
207
207
|
...props,
|
|
208
208
|
disabled: context.disabled || disabled,
|
|
@@ -409,4 +409,4 @@ function AskUser({ payload, result, isAnswered = false, isSubmitting = false, on
|
|
|
409
409
|
}
|
|
410
410
|
export { AskUserOptions as a, AskUserBody as c, AskUserOptionRow as d, AskUserOutput as f, AskUserOptionItem as i, AskUserContainer as l, AskUserQuestion as m, AskUserRoot as n, AskUserSubmit as o, AskUserPending as p, AskUserCustomAnswer as r, AskUserTextAnswer as s, AskUser as t, AskUserLabel as u };
|
|
411
411
|
|
|
412
|
-
//# sourceMappingURL=ask-user-
|
|
412
|
+
//# sourceMappingURL=ask-user-DPznrqGO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-user-DPznrqGO.js","names":["_Fragment"],"sources":["../src/ds/components/ai/ask-user/ask-user-context.ts","../src/ds/components/ai/ask-user/ask-user-layout.tsx","../src/ds/components/ai/ask-user/ask-user-controls.tsx","../src/ds/components/ai/ask-user/ask-user-root.tsx","../src/ds/components/ai/ask-user/ask-user-card.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\nimport type { AskUserSelectionMode } from './ask-user-types';\n\ninterface AskUserContextValue {\n questionId: string;\n selectionMode: AskUserSelectionMode;\n disabled: boolean;\n answerText: string;\n selectedOptionLabels: string[];\n isCustomAnswerSelected: boolean;\n canSubmitAnswer: boolean;\n selectOption: (optionLabel: string) => void;\n setCustomAnswerSelected: (selected: boolean) => void;\n changeAnswerText: (text: string) => void;\n unselectEmptyCustomAnswer: () => void;\n submitAnswer: () => void;\n}\n\nexport const AskUserContext = createContext<AskUserContextValue | undefined>(undefined);\n\nexport function useOptionalAskUserContext() {\n return useContext(AskUserContext);\n}\n\nexport function useAskUserContext() {\n const context = useOptionalAskUserContext();\n if (!context) throw new Error('AskUser controls must be rendered inside AskUser.Root.');\n return context;\n}\n","import { Check, MessageCircleQuestion } from 'lucide-react';\nimport type { ComponentProps, ReactNode } from 'react';\nimport { useOptionalAskUserContext } from './ask-user-context';\nimport type { AskUserResult } from './ask-user-types';\nimport { Badge } from '@/ds/components/Badge';\nimport { Button } from '@/ds/components/Button';\nimport { FieldItem, FieldLabel } from '@/ds/components/Field';\nimport type { FieldLabelProps } from '@/ds/components/Field';\nimport { Txt } from '@/ds/components/Txt';\nimport { Icon } from '@/ds/icons/Icon';\nimport { raisedSurfaceStyle } from '@/ds/primitives/raised-surface';\nimport { cn } from '@/lib/utils';\n\nexport const AskUserContainer = ({ className, ...props }: ComponentProps<'div'>) => (\n <div\n data-slot=\"ask-user\"\n className={cn(raisedSurfaceStyle, 'w-full overflow-hidden rounded-xl', className)}\n {...props}\n />\n);\n\nexport const AskUserLabel = ({ children = 'Question', className, ...props }: ComponentProps<'div'>) => (\n <div data-slot=\"ask-user-label\" className={cn('flex min-h-10 items-center gap-2 px-4 pt-3', className)} {...props}>\n <Icon size=\"xs\" className=\"text-muted-foreground\">\n <MessageCircleQuestion />\n </Icon>\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\">\n {children}\n </Txt>\n </div>\n);\n\nexport const AskUserBody = ({ className, ...props }: ComponentProps<'div'>) => (\n <div data-slot=\"ask-user-body\" className={cn('p-4', className)} {...props} />\n);\n\nconst AskUserQuestionTitle = ({ children }: { children: ReactNode }) => (\n <span className=\"flex items-start gap-2\">\n <Icon size=\"xs\" className={cn('mt-1 shrink-0', 'text-muted-foreground')} aria-hidden>\n <MessageCircleQuestion />\n </Icon>\n <span className=\"min-w-0\">{children}</span>\n </span>\n);\n\nexport const AskUserQuestion = ({ children, className, ...props }: ComponentProps<typeof Txt>) => {\n const context = useOptionalAskUserContext();\n return (\n <Txt\n as=\"p\"\n variant=\"subheading\"\n tone=\"ink\"\n {...props}\n id={context?.questionId ?? props.id}\n className={cn('mb-3', className)}\n >\n <AskUserQuestionTitle>{children}</AskUserQuestionTitle>\n </Txt>\n );\n};\n\nexport interface AskUserOptionRowProps extends Omit<FieldLabelProps, 'children'> {\n control: ReactNode;\n label: ReactNode;\n description?: string;\n disabled?: boolean;\n}\n\nexport const AskUserOptionRow = ({\n control,\n label,\n description,\n disabled = false,\n className,\n ...props\n}: AskUserOptionRowProps) => (\n <FieldItem disabled={disabled} className=\"contents\">\n <FieldLabel\n // state-layer's wash only stops at :disabled/aria-disabled, and a <label> is neither\n aria-disabled={disabled || undefined}\n className={cn('state-layer flex items-start gap-2.5 rounded-lg px-3 py-2 data-disabled:opacity-50', className)}\n {...props}\n >\n {control}\n <span className=\"grid gap-0.5\">\n <Txt as=\"span\" variant=\"body\" tone=\"ink\">\n {label}\n </Txt>\n {description ? (\n <Txt as=\"span\" variant=\"caption\" tone=\"muted\">\n {description}\n </Txt>\n ) : null}\n </span>\n </FieldLabel>\n </FieldItem>\n);\n\nexport type AskUserSubmitButtonProps = Omit<ComponentProps<typeof Button>, 'children'> & { children?: ReactNode };\n\nexport const AskUserSubmitButton = ({ children = 'Submit answer', ...props }: AskUserSubmitButtonProps) => (\n <Button icon={<Check />} type=\"button\" size=\"sm\" variant=\"primary\" {...props}>\n {children}\n </Button>\n);\n\nexport const AskUserPending = ({ children = 'Submitting…', ...props }: ComponentProps<typeof Txt>) => (\n <Txt as=\"span\" role=\"status\" variant=\"caption\" tone=\"muted\" {...props}>\n {children}\n </Txt>\n);\n\nexport interface AskUserOutputProps extends ComponentProps<'div'> {\n result: AskUserResult;\n}\n\nexport const AskUserOutput = ({ result, className, ...props }: AskUserOutputProps) => (\n <div\n data-slot=\"ask-user-output\"\n role={result.isError ? 'alert' : 'status'}\n className={cn('grid gap-2 rounded-lg bg-fill p-3', className)}\n {...props}\n >\n <Badge size=\"xs\" variant={result.isError ? 'destructive' : 'success'} className=\"justify-self-start\">\n {result.isError ? 'Error' : 'Answered'}\n </Badge>\n <Txt as=\"p\" variant=\"body\" tone=\"ink\" className={cn(result.isError && 'text-destructive-foreground')}>\n {result.content}\n </Txt>\n </div>\n);\n","import type { ChangeEvent, ComponentProps, KeyboardEvent, MouseEvent, ReactNode } from 'react';\nimport { useAskUserContext, useOptionalAskUserContext } from './ask-user-context';\nimport { AskUserOptionRow, AskUserSubmitButton } from './ask-user-layout';\nimport { Checkbox } from '@/ds/components/Checkbox';\nimport { FieldItem, FieldLabel, Fieldset } from '@/ds/components/Field';\nimport { Input } from '@/ds/components/Input';\nimport { RadioGroup, RadioGroupItem } from '@/ds/components/RadioGroup';\nimport { Txt } from '@/ds/components/Txt';\nimport { cn } from '@/lib/utils';\n\n// Keep the UI-only custom choice separate from suggested answer values.\nconst customAnswerValue = 'custom';\nconst optionValuePrefix = 'option:';\n\nfunction getOptionRadioValue(optionLabel: string): string {\n return `${optionValuePrefix}${optionLabel}`;\n}\n\nexport function AskUserOptions({ children, className, ...props }: ComponentProps<typeof Fieldset>) {\n const context = useAskUserContext();\n\n const handleSingleSelectChange = (radioValue: unknown) => {\n if (radioValue === customAnswerValue) {\n context.setCustomAnswerSelected(true);\n return;\n }\n if (typeof radioValue !== 'string' || !radioValue.startsWith(optionValuePrefix)) return;\n context.selectOption(radioValue.slice(optionValuePrefix.length));\n };\n\n if (context.selectionMode === 'multi_select') {\n return (\n <Fieldset aria-label=\"Answer options\" {...props} className={cn('gap-2', className)}>\n {children}\n </Fieldset>\n );\n }\n\n const selectedOptionLabel = context.selectedOptionLabels[0];\n const selectedOptionValue = selectedOptionLabel === undefined ? '' : getOptionRadioValue(selectedOptionLabel);\n const selectedRadioValue = context.isCustomAnswerSelected ? customAnswerValue : selectedOptionValue;\n\n return (\n <Fieldset\n {...props}\n className={cn('gap-2', className)}\n render={\n <RadioGroup\n aria-labelledby={context.questionId}\n disabled={context.disabled}\n value={selectedRadioValue}\n onValueChange={handleSingleSelectChange}\n />\n }\n >\n {children}\n </Fieldset>\n );\n}\n\nexport interface AskUserOptionItemProps {\n value: string;\n children: ReactNode;\n description?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function AskUserOptionItem({\n value,\n children,\n description,\n disabled = false,\n className,\n}: AskUserOptionItemProps) {\n const context = useAskUserContext();\n const isDisabled = context.disabled || disabled;\n\n const handleOptionToggle = () => {\n if (!isDisabled) context.selectOption(value);\n };\n\n return (\n <AskUserOptionRow\n label={children}\n description={description}\n disabled={isDisabled}\n className={className}\n control={\n context.selectionMode === 'multi_select' ? (\n <Checkbox\n className=\"mt-0.5\"\n disabled={isDisabled}\n checked={context.selectedOptionLabels.includes(value)}\n onCheckedChange={handleOptionToggle}\n />\n ) : (\n <RadioGroupItem className=\"mt-0.5\" disabled={isDisabled} value={getOptionRadioValue(value)} />\n )\n }\n />\n );\n}\n\nexport type AskUserTextAnswerProps = Omit<\n ComponentProps<typeof Input>,\n 'value' | 'defaultValue' | 'onChange' | 'onKeyDown'\n>;\n\nexport function AskUserTextAnswer({ disabled = false, ...props }: AskUserTextAnswerProps) {\n const context = useAskUserContext();\n\n const handleAnswerTextChange = (event: ChangeEvent<HTMLInputElement>) => {\n context.changeAnswerText(event.target.value);\n };\n\n const handleAnswerTextKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter' && !event.nativeEvent.isComposing) {\n event.preventDefault();\n context.submitAnswer();\n }\n };\n\n return (\n <Input\n type=\"text\"\n aria-labelledby={props['aria-label'] ? undefined : context.questionId}\n placeholder=\"Type your answer\"\n size=\"sm\"\n {...props}\n disabled={context.disabled || disabled}\n value={context.answerText}\n onChange={handleAnswerTextChange}\n onKeyDown={handleAnswerTextKeyDown}\n />\n );\n}\n\nexport interface AskUserCustomAnswerProps {\n className?: string;\n label?: string;\n inputLabel?: string;\n}\n\nexport function AskUserCustomAnswer({\n className,\n label = 'Other…',\n inputLabel = 'Your answer',\n}: AskUserCustomAnswerProps) {\n const context = useAskUserContext();\n\n const handleRowClick = (event: MouseEvent<HTMLDivElement>) => {\n if (context.disabled || event.target !== event.currentTarget) return;\n if (context.isCustomAnswerSelected) {\n event.currentTarget.querySelector<HTMLInputElement>('input[type=\"text\"]')?.focus();\n return;\n }\n event.currentTarget.querySelector<HTMLElement>('[role=\"radio\"], [role=\"checkbox\"]')?.click();\n };\n\n const handleControlMouseDown = (event: MouseEvent<HTMLSpanElement>) => {\n // Let the checkbox click unselect it before blur resets its controlled value.\n if (context.isCustomAnswerSelected && !context.answerText.trim()) event.preventDefault();\n };\n\n return (\n <FieldItem\n role=\"group\"\n aria-label=\"Custom answer\"\n aria-disabled={context.disabled || undefined}\n disabled={context.disabled}\n onClick={handleRowClick}\n className={cn(\n 'items-start gap-2.5 rounded-lg px-3 py-2 data-disabled:opacity-50',\n !context.isCustomAnswerSelected && 'state-layer',\n className,\n )}\n >\n {context.selectionMode === 'multi_select' ? (\n <Checkbox\n className=\"mt-0.5\"\n aria-label={label}\n disabled={context.disabled}\n checked={context.isCustomAnswerSelected}\n onMouseDown={handleControlMouseDown}\n onCheckedChange={context.setCustomAnswerSelected}\n />\n ) : (\n <RadioGroupItem aria-label={label} className=\"mt-0.5\" value={customAnswerValue} />\n )}\n {context.isCustomAnswerSelected ? (\n <AskUserTextAnswer\n aria-label={inputLabel}\n onBlur={context.unselectEmptyCustomAnswer}\n placeholder={label}\n variant=\"unstyled\"\n className={cn('h-auto min-w-0 flex-1 px-0', 'text-body')}\n autoFocus\n />\n ) : (\n <FieldLabel className=\"flex-1\">\n <Txt as=\"span\" variant=\"body\" tone=\"ink\">\n {label}\n </Txt>\n </FieldLabel>\n )}\n </FieldItem>\n );\n}\n\nexport type AskUserSubmitProps = ComponentProps<typeof AskUserSubmitButton> & {\n when?: 'always' | 'custom-answer';\n};\n\nexport function AskUserSubmit({ when = 'always', disabled = false, onClick, ...props }: AskUserSubmitProps) {\n const context = useOptionalAskUserContext();\n\n const handleSubmitClick = (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (!event.defaultPrevented && !disabled) context?.submitAnswer();\n };\n\n if (when === 'custom-answer' && !context?.isCustomAnswerSelected) return null;\n\n return (\n <AskUserSubmitButton\n {...props}\n disabled={disabled || (context !== undefined && !context.canSubmitAnswer)}\n onClick={handleSubmitClick}\n />\n );\n}\n","import { useId, useState } from 'react';\nimport type { ComponentProps } from 'react';\nimport { AskUserContext } from './ask-user-context';\nimport { AskUserContainer } from './ask-user-layout';\nimport type { AskUserAnswer, AskUserSelectionMode } from './ask-user-types';\n\nexport interface AskUserRootProps extends Omit<ComponentProps<typeof AskUserContainer>, 'onSubmit'> {\n selectionMode?: AskUserSelectionMode;\n disabled?: boolean;\n onSubmit: (answer: AskUserAnswer) => void;\n}\n\ninterface AskUserAnswerDraft {\n answerText: string;\n selectedOptionLabels: string[];\n selectionMode: AskUserSelectionMode;\n isCustomAnswerSelected: boolean;\n}\n\nfunction getAnswerToSubmit({\n answerText,\n selectedOptionLabels,\n selectionMode,\n isCustomAnswerSelected,\n}: AskUserAnswerDraft): AskUserAnswer | undefined {\n const trimmedAnswerText = answerText.trim();\n if (isCustomAnswerSelected && !trimmedAnswerText) return undefined;\n if (selectionMode === 'single_select') {\n // Suggested single-select answers already submit when selected.\n if (!isCustomAnswerSelected && selectedOptionLabels.length > 0) return undefined;\n return trimmedAnswerText || undefined;\n }\n\n const answerLabels = isCustomAnswerSelected ? [...selectedOptionLabels, trimmedAnswerText] : selectedOptionLabels;\n return answerLabels.length > 0 ? answerLabels : undefined;\n}\n\nfunction toggleSelectedOptionLabel(selectedOptionLabels: string[], optionLabel: string): string[] {\n if (selectedOptionLabels.includes(optionLabel)) {\n return selectedOptionLabels.filter(selectedLabel => selectedLabel !== optionLabel);\n }\n return [...selectedOptionLabels, optionLabel];\n}\n\nexport function AskUserRoot({\n selectionMode = 'single_select',\n disabled = false,\n onSubmit,\n children,\n ...props\n}: AskUserRootProps) {\n const questionId = useId();\n const [answerText, setAnswerText] = useState('');\n const [selectedOptionLabels, setSelectedOptionLabels] = useState<string[]>([]);\n const [isCustomAnswerSelected, setIsCustomAnswerSelected] = useState(false);\n const answerToSubmit = getAnswerToSubmit({\n answerText,\n selectedOptionLabels,\n selectionMode,\n isCustomAnswerSelected,\n });\n const canSubmitAnswer = !disabled && answerToSubmit !== undefined;\n\n const selectOption = (optionLabel: string) => {\n if (disabled) return;\n if (selectionMode === 'multi_select') {\n setSelectedOptionLabels(currentLabels => toggleSelectedOptionLabel(currentLabels, optionLabel));\n return;\n }\n setIsCustomAnswerSelected(false);\n setSelectedOptionLabels([optionLabel]);\n onSubmit(optionLabel);\n };\n\n const setCustomAnswerSelected = (selected: boolean) => {\n if (disabled) return;\n setIsCustomAnswerSelected(selected);\n if (selected && selectionMode === 'single_select') setSelectedOptionLabels([]);\n };\n\n const changeAnswerText = (text: string) => {\n if (!disabled) setAnswerText(text);\n };\n\n const unselectEmptyCustomAnswer = () => {\n if (!disabled && !answerText.trim()) setIsCustomAnswerSelected(false);\n };\n\n const submitAnswer = () => {\n if (disabled || answerToSubmit === undefined) return;\n onSubmit(answerToSubmit);\n };\n\n return (\n <AskUserContext.Provider\n value={{\n questionId,\n selectionMode,\n disabled,\n answerText,\n selectedOptionLabels,\n isCustomAnswerSelected,\n canSubmitAnswer,\n selectOption,\n setCustomAnswerSelected,\n changeAnswerText,\n unselectEmptyCustomAnswer,\n submitAnswer,\n }}\n >\n <AskUserContainer role=\"group\" aria-labelledby={questionId} {...props}>\n {children}\n </AskUserContainer>\n </AskUserContext.Provider>\n );\n}\n","import type { ComponentProps, ReactNode } from 'react';\nimport { useAskUserContext } from './ask-user-context';\nimport {\n AskUserCustomAnswer,\n AskUserOptionItem,\n AskUserOptions,\n AskUserSubmit,\n AskUserTextAnswer,\n} from './ask-user-controls';\nimport type { AskUserContainer } from './ask-user-layout';\nimport { AskUserBody, AskUserOutput, AskUserPending, AskUserQuestion } from './ask-user-layout';\nimport { AskUserRoot } from './ask-user-root';\nimport type { AskUserAnswer, AskUserOption, AskUserPayload, AskUserResult } from './ask-user-types';\nimport { Badge } from '@/ds/components/Badge';\n\nexport interface AskUserProps extends Omit<ComponentProps<typeof AskUserContainer>, 'children' | 'onSubmit'> {\n payload: AskUserPayload;\n result?: AskUserResult;\n isAnswered?: boolean;\n isSubmitting?: boolean;\n onSubmit: (answer: AskUserAnswer) => void;\n footer?: ReactNode;\n}\n\nfunction getValidOptions(options: AskUserPayload['options']): AskUserOption[] {\n return (\n options?.filter((option): option is AskUserOption =>\n Boolean(option && typeof option.label === 'string' && option.label),\n ) ?? []\n );\n}\n\nfunction AskUserAnswerControls({ options }: { options: AskUserOption[] }) {\n const { selectionMode } = useAskUserContext();\n const isMultiSelect = selectionMode === 'multi_select';\n if (options.length === 0) {\n return (\n <div className=\"flex items-center gap-2\">\n <AskUserTextAnswer />\n <AskUserSubmit className=\"shrink-0 whitespace-nowrap\" />\n </div>\n );\n }\n\n return (\n <>\n <AskUserOptions>\n {options.map(option => (\n <AskUserOptionItem key={option.label} value={option.label} description={option.description}>\n {option.label}\n </AskUserOptionItem>\n ))}\n <AskUserCustomAnswer />\n {isMultiSelect ? <AskUserSubmit className=\"mt-1 justify-self-start\" /> : null}\n </AskUserOptions>\n {!isMultiSelect ? <AskUserSubmit when=\"custom-answer\" className=\"mt-2\" /> : null}\n </>\n );\n}\n\ninterface AskUserCardContentProps extends Pick<AskUserProps, 'result' | 'isAnswered' | 'isSubmitting' | 'footer'> {\n options: AskUserOption[];\n}\n\nfunction AskUserCardContent({ result, isAnswered, isSubmitting, options, footer }: AskUserCardContentProps) {\n if (result) return <AskUserOutput result={result} />;\n if (isAnswered) return <Badge variant=\"success\">Answered</Badge>;\n\n return (\n <>\n <AskUserAnswerControls options={options} />\n {isSubmitting ? <AskUserPending className=\"mt-3 block\" /> : null}\n {footer}\n </>\n );\n}\n\nexport function AskUser({\n payload,\n result,\n isAnswered = false,\n isSubmitting = false,\n onSubmit,\n footer,\n ...props\n}: AskUserProps) {\n const options = getValidOptions(payload.options);\n const isMultiSelect = options.length > 0 && payload.selectionMode === 'multi_select';\n const payloadKey = JSON.stringify([payload.question, options.map(option => option.label), payload.selectionMode]);\n const hasAnswer = result !== undefined || isAnswered;\n\n return (\n <AskUserRoot\n key={payloadKey}\n data-testid=\"ask-user\"\n selectionMode={isMultiSelect ? 'multi_select' : 'single_select'}\n disabled={isSubmitting || hasAnswer}\n onSubmit={onSubmit}\n {...props}\n >\n <AskUserBody>\n <AskUserQuestion>{payload.question}</AskUserQuestion>\n <AskUserCardContent\n result={result}\n isAnswered={isAnswered}\n isSubmitting={isSubmitting}\n options={options}\n footer={footer}\n />\n </AskUserBody>\n </AskUserRoot>\n );\n}\n"],"mappings":"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"}
|
package/dist/chart-plot.css
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* The plot box is a size container; the chart inside is drawn at `--chart-drawn-w/h` and scaled to
|
|
3
|
-
* the box's live size
|
|
4
|
-
*
|
|
3
|
+
* the box's live size `--chart-box-w/h`, so between redraws it moves with its card. Both are plain
|
|
4
|
+
* numbers of CSS pixels: Safari resolves `100cqw / 539px` against different zoom levels, so a zoomed
|
|
5
|
+
* page stretched every chart by the zoom factor. At rest the two sizes match and the scale is 1.
|
|
5
6
|
*/
|
|
6
7
|
.chart-plot-box {
|
|
7
8
|
container-type: size;
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
|
|
25
26
|
.chart-plot-stretch {
|
|
26
27
|
transform-origin: 0 0;
|
|
27
|
-
transform: scale(
|
|
28
|
+
transform: scale(calc(var(--chart-box-w) / var(--chart-drawn-w)), calc(var(--chart-box-h) / var(--chart-drawn-h)));
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/*
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
.chart-plot-stretch .recharts-surface text:not([transform]) {
|
|
36
37
|
transform-box: fill-box;
|
|
37
38
|
transform-origin: center;
|
|
38
|
-
transform: scale(
|
|
39
|
+
transform: scale(calc(var(--chart-drawn-w) / var(--chart-box-w)), calc(var(--chart-drawn-h) / var(--chart-box-h)));
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
.chart-plot-stretch .recharts-surface text[text-anchor='start']:not([transform]) {
|
|
@@ -2,7 +2,7 @@ import { t as cn } from "./utils-DQx8TpNT.js";
|
|
|
2
2
|
import { deprecatedErrorAria } from "./primitives/form-element.es.js";
|
|
3
3
|
import { s as isIconButtonSize, t as Button } from "./Button-CLXTbAY8.js";
|
|
4
4
|
import { n as keepOwnAccessibleName, r as useFieldAriaIds } from "./field-control-aria-BaRSUUMR.js";
|
|
5
|
-
import { S as comboboxStyles, _ as ComboboxRoot, b as ComboboxTrigger, g as ComboboxList, l as ComboboxInput, n as ComboboxChips, o as ComboboxContent, r as ComboboxChipsInput, s as ComboboxEmpty, t as ComboboxChip, u as ComboboxItem, v as ComboboxSearch, x as ComboboxValue } from "./combobox-parts-
|
|
5
|
+
import { S as comboboxStyles, _ as ComboboxRoot, b as ComboboxTrigger, g as ComboboxList, l as ComboboxInput, n as ComboboxChips, o as ComboboxContent, r as ComboboxChipsInput, s as ComboboxEmpty, t as ComboboxChip, u as ComboboxItem, v as ComboboxSearch, x as ComboboxValue } from "./combobox-parts-D90ir0Py.js";
|
|
6
6
|
import { n as FieldBlock, t as fieldErrorId } from "./field-error-id-DBSuJ3HM.js";
|
|
7
7
|
import * as React$1 from "react";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -159,7 +159,7 @@ function MultipleComboboxControl({ shape, controlProps, placeholder, selectedCou
|
|
|
159
159
|
})
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
|
-
function SingleCombobox({ shape, controlProps, options, value, onValueChange, placeholder = "Select option
|
|
162
|
+
function SingleCombobox({ shape, controlProps, options, value, onValueChange, placeholder = "Select option", searchPlaceholder = "Search", emptyText = "No option found.", className, disabled = false, variant = "default", size = "md", open, onOpenChange, container, align = "start", showChevron = true, iconOnlyValue = false, allowCustomValue = false, onInputValueChange }) {
|
|
163
163
|
const [inputValue, setInputValue] = React$1.useState("");
|
|
164
164
|
const filter = useOptionFilter();
|
|
165
165
|
const inField = shape === "field";
|
|
@@ -208,7 +208,7 @@ function SingleCombobox({ shape, controlProps, options, value, onValueChange, pl
|
|
|
208
208
|
})]
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
|
-
function MultipleCombobox({ shape, controlProps, options, value = EMPTY_VALUES, onValueChange, clearLabel, placeholder = "Select options
|
|
211
|
+
function MultipleCombobox({ shape, controlProps, options, value = EMPTY_VALUES, onValueChange, clearLabel, placeholder = "Select options", searchPlaceholder = "Search", emptyText = "No option found.", className, disabled = false, variant = "default", size = "md", open, onOpenChange, container, align = "start", showChevron = true, iconOnlyValue = false }) {
|
|
212
212
|
const filter = useOptionFilter();
|
|
213
213
|
const selectedValues = new Set(value);
|
|
214
214
|
const selectedOptions = options.filter((option) => selectedValues.has(option.value));
|
|
@@ -299,4 +299,4 @@ function Combobox$1(props) {
|
|
|
299
299
|
}
|
|
300
300
|
export { Combobox$1 as t };
|
|
301
301
|
|
|
302
|
-
//# sourceMappingURL=combobox-
|
|
302
|
+
//# sourceMappingURL=combobox-D21no8t5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox-CtCK0mNx.js","names":["BaseCombobox","_Fragment","React","Combobox"],"sources":["../src/ds/components/Combobox/combobox.tsx"],"sourcesContent":["import { Combobox as BaseCombobox } from '@base-ui/react/combobox';\nimport { X } from 'lucide-react';\nimport * as React from 'react';\nimport {\n ComboboxChip,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxRoot,\n ComboboxSearch,\n ComboboxTrigger,\n ComboboxValue,\n} from './combobox-parts';\nimport { comboboxStyles } from './combobox-styles';\nimport type { ComboboxVariant } from './combobox-styles';\nimport { Button, isIconButtonSize } from '@/ds/components/Button/Button';\nimport type { ButtonSize } from '@/ds/components/Button/Button';\nimport { keepOwnAccessibleName, useFieldAriaIds } from '@/ds/components/Field/field-control-aria';\nimport { FieldBlock } from '@/ds/components/FormFieldBlocks/block/field-block';\nimport { fieldErrorId } from '@/ds/components/FormFieldBlocks/block/field-error-id';\nimport { deprecatedErrorAria } from '@/ds/primitives/form-element';\nimport type { PortalContainer } from '@/ds/primitives/portal-container';\nimport { cn } from '@/lib/utils';\n\nexport type { ComboboxVariant } from './combobox-styles';\n\nexport type ComboboxOption = {\n label: string;\n value: string;\n description?: string;\n displayLabel?: React.ReactNode;\n start?: React.ReactNode;\n end?: React.ReactNode;\n /** Extra terms the search matches besides `label` (aliases, ids, a provider name). Never displayed. */\n keywords?: readonly string[];\n};\n\ntype ComboboxSharedProps = {\n options: ComboboxOption[];\n placeholder?: React.ReactNode;\n /** The search row's placeholder for compact triggers. A field (inside a `Field`) searches in itself and shows `placeholder`. */\n searchPlaceholder?: string;\n emptyText?: string;\n className?: string;\n disabled?: boolean;\n /**\n * `default` inside a `Field` is a form field you type in to search. `ghost` (and the legacy `outline`/`link`),\n * or any variant outside a `Field`, is a compact trigger with the search inside the popup.\n */\n variant?: ComboboxVariant;\n /** Icon sizes (`icon-*`) render a chevron-only trigger with the search in the popup; pass `aria-label` to name it. */\n size?: ButtonSize;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n container?: PortalContainer;\n id?: string;\n 'aria-label'?: string;\n 'aria-describedby'?: string;\n /** Which edge of the trigger the popup lines up with. `end` opens it leftwards (e.g. an icon trigger at the end of a row). */\n align?: 'start' | 'center' | 'end';\n showChevron?: boolean;\n iconOnlyValue?: boolean;\n allowCustomValue?: boolean;\n /** Called with the search text as it changes (and with `''` after a compact trigger's selection resets it). */\n onInputValueChange?: (value: string) => void;\n /** @deprecated Wrap the combobox in `<Field invalid>` with a `FieldError`. */\n error?: string;\n /** @deprecated Only keyed the `error` message id; a `Field` links its error without one. */\n name?: string;\n};\n\nexport type ComboboxSingleProps = ComboboxSharedProps & {\n multiple?: false;\n value?: string;\n onValueChange?: (value: string) => void;\n};\n\nexport type ComboboxMultipleProps = ComboboxSharedProps & {\n multiple: true;\n value?: readonly string[];\n onValueChange?: (value: string[]) => void;\n clearLabel?: string;\n};\n\nexport type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;\n\nconst EMPTY_VALUES: readonly string[] = [];\n\nfunction isMultipleCombobox(props: ComboboxProps): props is ComboboxMultipleProps {\n return props.multiple === true;\n}\n\n/** The text a field shows for its value: a plain-text `displayLabel` wins over `label`. */\nfunction optionText(option: ComboboxOption) {\n return typeof option.displayLabel === 'string' ? option.displayLabel : option.label;\n}\n\n/** Inputs take a text placeholder only; a ReactNode placeholder keeps the compact trigger. */\nfunction textPlaceholder(placeholder: React.ReactNode) {\n return typeof placeholder === 'string' ? placeholder : undefined;\n}\n\n/**\n * How the combobox renders:\n * - `field`: a single select inside a `Field` searches in the field, like a text input.\n * - `chips`: a multiple select inside a `Field` shows its picks as chips and searches in the field.\n * - `trigger`: everything else (toolbars, breadcrumbs, inline pickers that callers lay out as a button)\n * keeps a compact trigger with the search inside the popup.\n */\ntype ComboboxShape = 'field' | 'chips' | 'trigger';\n\nfunction getComboboxShape({\n inFieldContext,\n multiple,\n variant,\n size,\n iconOnlyValue,\n showChevron,\n placeholder,\n}: {\n inFieldContext: boolean;\n multiple: boolean;\n variant: ComboboxVariant;\n size: ButtonSize;\n iconOnlyValue: boolean;\n showChevron: boolean;\n placeholder: React.ReactNode;\n}): ComboboxShape {\n if (!inFieldContext) return 'trigger';\n if (variant !== 'default') return 'trigger';\n if (isIconButtonSize(size)) return 'trigger';\n if (iconOnlyValue || !showChevron) return 'trigger';\n if (placeholder !== undefined && textPlaceholder(placeholder) === undefined) return 'trigger';\n if (!multiple) return 'field';\n // The chips field is tuned to the medium control height.\n if (size !== 'md') return 'trigger';\n return 'chips';\n}\n\n/** A \"Use “text”\" option for typed text that isn't one of the options yet. */\nfunction getCustomOption(options: ComboboxOption[], inputValue: string): ComboboxOption | undefined {\n const value = inputValue.trim();\n if (!value) return undefined;\n // A field shows the selected option's text while it's closed; that isn't a new value.\n if (options.some(option => option.value === value || optionText(option) === value)) return undefined;\n return { label: `Use “${value}”`, value };\n}\n\n/** Matches the label like Base UI's default filter, the text a field shows, and any keywords. */\nfunction useOptionFilter() {\n const { contains } = BaseCombobox.useFilter();\n return (option: ComboboxOption, query: string) =>\n [option.label, optionText(option), ...(option.keywords ?? [])].some(text => contains(text, query));\n}\n\n/** Names the control when nothing else does: an `id` or a `Field` links a label to it instead. */\nfunction getFallbackAriaLabel({ hasLabel, multiple }: { hasLabel: boolean; multiple: boolean }) {\n if (hasLabel) return undefined;\n return multiple ? 'Select options' : 'Select option';\n}\n\nfunction getControlProps({\n id,\n ariaLabel,\n ariaDescribedBy,\n hasLabel,\n multiple,\n error,\n errorName,\n}: {\n id?: string;\n ariaLabel?: string;\n ariaDescribedBy?: string;\n hasLabel: boolean;\n multiple: boolean;\n error?: string;\n errorName: string;\n}) {\n const describedBy = [ariaDescribedBy, error ? fieldErrorId(errorName) : undefined].filter(Boolean).join(' ');\n return {\n id,\n 'aria-label': ariaLabel ?? getFallbackAriaLabel({ hasLabel, multiple }),\n ...keepOwnAccessibleName({ 'aria-label': ariaLabel }),\n ...deprecatedErrorAria(Boolean(error)),\n 'aria-describedby': describedBy || undefined,\n };\n}\n\ntype ControlProps = ReturnType<typeof getControlProps>;\n\nfunction triggerClassName({ iconOnlyValue, className }: { iconOnlyValue: boolean; className?: string }) {\n return cn(iconOnlyValue && 'w-auto px-2.5', className);\n}\n\nfunction ComboboxOptionText({ option }: { option: ComboboxOption }) {\n return (\n <span className={comboboxStyles.optionText}>\n <span className={comboboxStyles.optionLabel}>{option.label}</span>\n {option.description && <span className={comboboxStyles.optionDescription}>{option.description}</span>}\n </span>\n );\n}\n\nfunction ComboboxClearFooter({ label, onClear }: { label: string; onClear: () => void }) {\n return (\n <div className=\"border-t border-border p-1\">\n <Button\n type=\"button\"\n variant=\"destructive-ghost\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={onClear}\n icon={<X />}\n >\n {label}\n </Button>\n </div>\n );\n}\n\n/** The popup: the search row for a compact trigger (a field searches in itself), then the options. */\nfunction ComboboxOptionsContent({\n showSearch,\n searchPlaceholder,\n emptyText,\n align,\n container,\n footer,\n}: {\n showSearch: boolean;\n searchPlaceholder: string;\n emptyText: string;\n align: 'start' | 'center' | 'end';\n container?: PortalContainer;\n footer?: React.ReactNode;\n}) {\n return (\n <ComboboxContent align={align} container={container}>\n {showSearch ? <ComboboxSearch placeholder={searchPlaceholder} /> : null}\n <ComboboxEmpty>{emptyText}</ComboboxEmpty>\n <ComboboxList>\n {(option: ComboboxOption) => (\n <ComboboxItem key={option.value} value={option}>\n {option.start}\n <ComboboxOptionText option={option} />\n {option.end ? <span className={comboboxStyles.optionEnd}>{option.end}</span> : null}\n </ComboboxItem>\n )}\n </ComboboxList>\n {footer}\n </ComboboxContent>\n );\n}\n\ntype TriggerLookProps = {\n variant: ComboboxVariant;\n size: ButtonSize;\n showChevron: boolean;\n iconOnlyValue: boolean;\n className?: string;\n};\n\nfunction SingleComboboxControl({\n shape,\n controlProps,\n placeholder,\n selectedOption,\n look,\n}: {\n shape: ComboboxShape;\n controlProps: ControlProps;\n placeholder: React.ReactNode;\n selectedOption?: ComboboxOption;\n look: TriggerLookProps;\n}) {\n if (shape === 'field') {\n return (\n <ComboboxInput\n {...controlProps}\n size={isIconButtonSize(look.size) ? undefined : look.size}\n placeholder={textPlaceholder(placeholder)}\n start={selectedOption?.start}\n className={look.className}\n />\n );\n }\n\n return (\n <ComboboxTrigger\n {...controlProps}\n variant={look.variant}\n size={look.size}\n showChevron={look.showChevron}\n icon={isIconButtonSize(look.size) ? undefined : selectedOption?.start}\n className={triggerClassName(look)}\n >\n {selectedOption?.displayLabel ?? <ComboboxValue placeholder={placeholder} />}\n </ComboboxTrigger>\n );\n}\n\nfunction MultipleComboboxControl({\n shape,\n controlProps,\n placeholder,\n selectedCount,\n look,\n}: {\n shape: ComboboxShape;\n controlProps: ControlProps;\n placeholder: React.ReactNode;\n selectedCount: number;\n look: TriggerLookProps;\n}) {\n if (shape === 'chips') {\n return (\n <ComboboxChips className={look.className}>\n <ComboboxValue>\n {(items: ComboboxOption[]) => (\n <>\n {items.map(item => (\n <ComboboxChip key={item.value}>{item.label}</ComboboxChip>\n ))}\n <ComboboxChipsInput\n {...controlProps}\n placeholder={items.length === 0 ? textPlaceholder(placeholder) : undefined}\n />\n </>\n )}\n </ComboboxValue>\n </ComboboxChips>\n );\n }\n\n const text = selectedCount === 0 ? placeholder : `${selectedCount} selected`;\n return (\n <ComboboxTrigger\n {...controlProps}\n variant={look.variant}\n size={look.size}\n showChevron={look.showChevron}\n className={triggerClassName(look)}\n >\n <span className={cn(selectedCount === 0 && comboboxStyles.placeholder)}>{text}</span>\n </ComboboxTrigger>\n );\n}\n\ntype ShapeProps = { shape: ComboboxShape; controlProps: ControlProps };\n\nfunction SingleCombobox({\n shape,\n controlProps,\n options,\n value,\n onValueChange,\n placeholder = 'Select option...',\n searchPlaceholder = 'Search...',\n emptyText = 'No option found.',\n className,\n disabled = false,\n variant = 'default',\n size = 'md',\n open,\n onOpenChange,\n container,\n align = 'start',\n showChevron = true,\n iconOnlyValue = false,\n allowCustomValue = false,\n onInputValueChange,\n}: ComboboxSingleProps & ShapeProps) {\n // The search text: a field's own text, or the compact trigger's popup search.\n const [inputValue, setInputValue] = React.useState('');\n const filter = useOptionFilter();\n const inField = shape === 'field';\n const selectedOption = options.find(option => option.value === value);\n const customOption = allowCustomValue ? getCustomOption(options, inputValue) : undefined;\n const items = customOption ? [customOption, ...options] : options;\n\n function handleInputValueChange(next: string) {\n setInputValue(next);\n onInputValueChange?.(next);\n }\n\n function handleValueChange(item: ComboboxOption | null) {\n if (!item) return;\n onValueChange?.(item.value);\n // A field shows the picked option; a compact trigger's popup search starts empty next time.\n if (!inField) handleInputValueChange('');\n }\n\n return (\n <ComboboxRoot\n autoHighlight\n items={items}\n filter={filter}\n itemToStringLabel={optionText}\n // Base UI's \"nothing selected\" is null.\n value={selectedOption ?? null}\n // A field owns its text (the selected option's, or the search); a compact trigger's search is reset here.\n inputValue={inField ? undefined : inputValue}\n onInputValueChange={handleInputValueChange}\n onValueChange={handleValueChange}\n disabled={disabled}\n open={open}\n onOpenChange={onOpenChange}\n >\n <SingleComboboxControl\n shape={shape}\n controlProps={controlProps}\n placeholder={placeholder}\n selectedOption={selectedOption}\n look={{ variant, size, showChevron, iconOnlyValue, className }}\n />\n <ComboboxOptionsContent\n showSearch={!inField}\n searchPlaceholder={searchPlaceholder}\n emptyText={emptyText}\n align={align}\n container={container}\n />\n </ComboboxRoot>\n );\n}\n\nfunction MultipleCombobox({\n shape,\n controlProps,\n options,\n value = EMPTY_VALUES,\n onValueChange,\n clearLabel,\n placeholder = 'Select options...',\n searchPlaceholder = 'Search...',\n emptyText = 'No option found.',\n className,\n disabled = false,\n variant = 'default',\n size = 'md',\n open,\n onOpenChange,\n container,\n align = 'start',\n showChevron = true,\n iconOnlyValue = false,\n}: ComboboxMultipleProps & ShapeProps) {\n const filter = useOptionFilter();\n const selectedValues = new Set(value);\n const selectedOptions = options.filter(option => selectedValues.has(option.value));\n const footer =\n clearLabel && selectedOptions.length > 0 ? (\n <ComboboxClearFooter label={clearLabel} onClear={() => onValueChange?.([])} />\n ) : undefined;\n\n return (\n <ComboboxRoot\n multiple\n autoHighlight\n items={options}\n filter={filter}\n value={selectedOptions}\n onValueChange={items => onValueChange?.(items.map(item => item.value))}\n disabled={disabled}\n open={open}\n onOpenChange={onOpenChange}\n >\n <MultipleComboboxControl\n shape={shape}\n controlProps={controlProps}\n placeholder={placeholder}\n selectedCount={selectedOptions.length}\n look={{ variant, size, showChevron, iconOnlyValue, className }}\n />\n <ComboboxOptionsContent\n showSearch={shape !== 'chips'}\n searchPlaceholder={searchPlaceholder}\n emptyText={emptyText}\n align={align}\n container={container}\n footer={footer}\n />\n </ComboboxRoot>\n );\n}\n\n/**\n * A searchable select driven by `options`, built on the Combobox parts.\n *\n * Inside a `Field`, a `default` combobox searches right in the field: a text field for a single value,\n * removable chips for `multiple`. Everywhere else (outside a `Field`, `ghost` and icon-size triggers,\n * custom non-text placeholders) it keeps a compact trigger with the search inside the popup. For anything\n * these props don't cover (groups, rich items), compose the parts directly (`ComboboxRoot`, ...).\n */\nexport function Combobox(props: ComboboxProps) {\n const field = useFieldAriaIds();\n const generatedName = React.useId();\n const multiple = isMultipleCombobox(props);\n const shape = getComboboxShape({\n inFieldContext: Boolean(field),\n multiple,\n variant: props.variant ?? 'default',\n size: props.size ?? 'md',\n iconOnlyValue: props.iconOnlyValue ?? false,\n showChevron: props.showChevron ?? true,\n placeholder: props.placeholder,\n });\n const errorName = props.name ?? generatedName;\n const controlProps = getControlProps({\n id: props.id,\n ariaLabel: props['aria-label'],\n ariaDescribedBy: props['aria-describedby'],\n hasLabel: Boolean(props.id || field),\n multiple,\n error: props.error,\n errorName,\n });\n\n const root = isMultipleCombobox(props) ? (\n <MultipleCombobox {...props} shape={shape} controlProps={controlProps} />\n ) : (\n <SingleCombobox {...props} shape={shape} controlProps={controlProps} />\n );\n\n if (!props.error) return root;\n\n return (\n <div className={comboboxStyles.root}>\n {root}\n <FieldBlock.ErrorMsg name={errorName}>{props.error}</FieldBlock.ErrorMsg>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"combobox-D21no8t5.js","names":["BaseCombobox","_Fragment","React","Combobox"],"sources":["../src/ds/components/Combobox/combobox.tsx"],"sourcesContent":["import { Combobox as BaseCombobox } from '@base-ui/react/combobox';\nimport { X } from 'lucide-react';\nimport * as React from 'react';\nimport {\n ComboboxChip,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxInput,\n ComboboxItem,\n ComboboxList,\n ComboboxRoot,\n ComboboxSearch,\n ComboboxTrigger,\n ComboboxValue,\n} from './combobox-parts';\nimport { comboboxStyles } from './combobox-styles';\nimport type { ComboboxVariant } from './combobox-styles';\nimport { Button, isIconButtonSize } from '@/ds/components/Button/Button';\nimport type { ButtonSize } from '@/ds/components/Button/Button';\nimport { keepOwnAccessibleName, useFieldAriaIds } from '@/ds/components/Field/field-control-aria';\nimport { FieldBlock } from '@/ds/components/FormFieldBlocks/block/field-block';\nimport { fieldErrorId } from '@/ds/components/FormFieldBlocks/block/field-error-id';\nimport { deprecatedErrorAria } from '@/ds/primitives/form-element';\nimport type { PortalContainer } from '@/ds/primitives/portal-container';\nimport { cn } from '@/lib/utils';\n\nexport type { ComboboxVariant } from './combobox-styles';\n\nexport type ComboboxOption = {\n label: string;\n value: string;\n description?: string;\n displayLabel?: React.ReactNode;\n start?: React.ReactNode;\n end?: React.ReactNode;\n /** Extra terms the search matches besides `label` (aliases, ids, a provider name). Never displayed. */\n keywords?: readonly string[];\n};\n\ntype ComboboxSharedProps = {\n options: ComboboxOption[];\n placeholder?: React.ReactNode;\n /** The search row's placeholder for compact triggers. A field (inside a `Field`) searches in itself and shows `placeholder`. */\n searchPlaceholder?: string;\n emptyText?: string;\n className?: string;\n disabled?: boolean;\n /**\n * `default` inside a `Field` is a form field you type in to search. `ghost` (and the legacy `outline`/`link`),\n * or any variant outside a `Field`, is a compact trigger with the search inside the popup.\n */\n variant?: ComboboxVariant;\n /** Icon sizes (`icon-*`) render a chevron-only trigger with the search in the popup; pass `aria-label` to name it. */\n size?: ButtonSize;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n container?: PortalContainer;\n id?: string;\n 'aria-label'?: string;\n 'aria-describedby'?: string;\n /** Which edge of the trigger the popup lines up with. `end` opens it leftwards (e.g. an icon trigger at the end of a row). */\n align?: 'start' | 'center' | 'end';\n showChevron?: boolean;\n iconOnlyValue?: boolean;\n allowCustomValue?: boolean;\n /** Called with the search text as it changes (and with `''` after a compact trigger's selection resets it). */\n onInputValueChange?: (value: string) => void;\n /** @deprecated Wrap the combobox in `<Field invalid>` with a `FieldError`. */\n error?: string;\n /** @deprecated Only keyed the `error` message id; a `Field` links its error without one. */\n name?: string;\n};\n\nexport type ComboboxSingleProps = ComboboxSharedProps & {\n multiple?: false;\n value?: string;\n onValueChange?: (value: string) => void;\n};\n\nexport type ComboboxMultipleProps = ComboboxSharedProps & {\n multiple: true;\n value?: readonly string[];\n onValueChange?: (value: string[]) => void;\n clearLabel?: string;\n};\n\nexport type ComboboxProps = ComboboxSingleProps | ComboboxMultipleProps;\n\nconst EMPTY_VALUES: readonly string[] = [];\n\nfunction isMultipleCombobox(props: ComboboxProps): props is ComboboxMultipleProps {\n return props.multiple === true;\n}\n\n/** The text a field shows for its value: a plain-text `displayLabel` wins over `label`. */\nfunction optionText(option: ComboboxOption) {\n return typeof option.displayLabel === 'string' ? option.displayLabel : option.label;\n}\n\n/** Inputs take a text placeholder only; a ReactNode placeholder keeps the compact trigger. */\nfunction textPlaceholder(placeholder: React.ReactNode) {\n return typeof placeholder === 'string' ? placeholder : undefined;\n}\n\n/**\n * How the combobox renders:\n * - `field`: a single select inside a `Field` searches in the field, like a text input.\n * - `chips`: a multiple select inside a `Field` shows its picks as chips and searches in the field.\n * - `trigger`: everything else (toolbars, breadcrumbs, inline pickers that callers lay out as a button)\n * keeps a compact trigger with the search inside the popup.\n */\ntype ComboboxShape = 'field' | 'chips' | 'trigger';\n\nfunction getComboboxShape({\n inFieldContext,\n multiple,\n variant,\n size,\n iconOnlyValue,\n showChevron,\n placeholder,\n}: {\n inFieldContext: boolean;\n multiple: boolean;\n variant: ComboboxVariant;\n size: ButtonSize;\n iconOnlyValue: boolean;\n showChevron: boolean;\n placeholder: React.ReactNode;\n}): ComboboxShape {\n if (!inFieldContext) return 'trigger';\n if (variant !== 'default') return 'trigger';\n if (isIconButtonSize(size)) return 'trigger';\n if (iconOnlyValue || !showChevron) return 'trigger';\n if (placeholder !== undefined && textPlaceholder(placeholder) === undefined) return 'trigger';\n if (!multiple) return 'field';\n // The chips field is tuned to the medium control height.\n if (size !== 'md') return 'trigger';\n return 'chips';\n}\n\n/** A \"Use “text”\" option for typed text that isn't one of the options yet. */\nfunction getCustomOption(options: ComboboxOption[], inputValue: string): ComboboxOption | undefined {\n const value = inputValue.trim();\n if (!value) return undefined;\n // A field shows the selected option's text while it's closed; that isn't a new value.\n if (options.some(option => option.value === value || optionText(option) === value)) return undefined;\n return { label: `Use “${value}”`, value };\n}\n\n/** Matches the label like Base UI's default filter, the text a field shows, and any keywords. */\nfunction useOptionFilter() {\n const { contains } = BaseCombobox.useFilter();\n return (option: ComboboxOption, query: string) =>\n [option.label, optionText(option), ...(option.keywords ?? [])].some(text => contains(text, query));\n}\n\n/** Names the control when nothing else does: an `id` or a `Field` links a label to it instead. */\nfunction getFallbackAriaLabel({ hasLabel, multiple }: { hasLabel: boolean; multiple: boolean }) {\n if (hasLabel) return undefined;\n return multiple ? 'Select options' : 'Select option';\n}\n\nfunction getControlProps({\n id,\n ariaLabel,\n ariaDescribedBy,\n hasLabel,\n multiple,\n error,\n errorName,\n}: {\n id?: string;\n ariaLabel?: string;\n ariaDescribedBy?: string;\n hasLabel: boolean;\n multiple: boolean;\n error?: string;\n errorName: string;\n}) {\n const describedBy = [ariaDescribedBy, error ? fieldErrorId(errorName) : undefined].filter(Boolean).join(' ');\n return {\n id,\n 'aria-label': ariaLabel ?? getFallbackAriaLabel({ hasLabel, multiple }),\n ...keepOwnAccessibleName({ 'aria-label': ariaLabel }),\n ...deprecatedErrorAria(Boolean(error)),\n 'aria-describedby': describedBy || undefined,\n };\n}\n\ntype ControlProps = ReturnType<typeof getControlProps>;\n\nfunction triggerClassName({ iconOnlyValue, className }: { iconOnlyValue: boolean; className?: string }) {\n return cn(iconOnlyValue && 'w-auto px-2.5', className);\n}\n\nfunction ComboboxOptionText({ option }: { option: ComboboxOption }) {\n return (\n <span className={comboboxStyles.optionText}>\n <span className={comboboxStyles.optionLabel}>{option.label}</span>\n {option.description && <span className={comboboxStyles.optionDescription}>{option.description}</span>}\n </span>\n );\n}\n\nfunction ComboboxClearFooter({ label, onClear }: { label: string; onClear: () => void }) {\n return (\n <div className=\"border-t border-border p-1\">\n <Button\n type=\"button\"\n variant=\"destructive-ghost\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={onClear}\n icon={<X />}\n >\n {label}\n </Button>\n </div>\n );\n}\n\n/** The popup: the search row for a compact trigger (a field searches in itself), then the options. */\nfunction ComboboxOptionsContent({\n showSearch,\n searchPlaceholder,\n emptyText,\n align,\n container,\n footer,\n}: {\n showSearch: boolean;\n searchPlaceholder: string;\n emptyText: string;\n align: 'start' | 'center' | 'end';\n container?: PortalContainer;\n footer?: React.ReactNode;\n}) {\n return (\n <ComboboxContent align={align} container={container}>\n {showSearch ? <ComboboxSearch placeholder={searchPlaceholder} /> : null}\n <ComboboxEmpty>{emptyText}</ComboboxEmpty>\n <ComboboxList>\n {(option: ComboboxOption) => (\n <ComboboxItem key={option.value} value={option}>\n {option.start}\n <ComboboxOptionText option={option} />\n {option.end ? <span className={comboboxStyles.optionEnd}>{option.end}</span> : null}\n </ComboboxItem>\n )}\n </ComboboxList>\n {footer}\n </ComboboxContent>\n );\n}\n\ntype TriggerLookProps = {\n variant: ComboboxVariant;\n size: ButtonSize;\n showChevron: boolean;\n iconOnlyValue: boolean;\n className?: string;\n};\n\nfunction SingleComboboxControl({\n shape,\n controlProps,\n placeholder,\n selectedOption,\n look,\n}: {\n shape: ComboboxShape;\n controlProps: ControlProps;\n placeholder: React.ReactNode;\n selectedOption?: ComboboxOption;\n look: TriggerLookProps;\n}) {\n if (shape === 'field') {\n return (\n <ComboboxInput\n {...controlProps}\n size={isIconButtonSize(look.size) ? undefined : look.size}\n placeholder={textPlaceholder(placeholder)}\n start={selectedOption?.start}\n className={look.className}\n />\n );\n }\n\n return (\n <ComboboxTrigger\n {...controlProps}\n variant={look.variant}\n size={look.size}\n showChevron={look.showChevron}\n icon={isIconButtonSize(look.size) ? undefined : selectedOption?.start}\n className={triggerClassName(look)}\n >\n {selectedOption?.displayLabel ?? <ComboboxValue placeholder={placeholder} />}\n </ComboboxTrigger>\n );\n}\n\nfunction MultipleComboboxControl({\n shape,\n controlProps,\n placeholder,\n selectedCount,\n look,\n}: {\n shape: ComboboxShape;\n controlProps: ControlProps;\n placeholder: React.ReactNode;\n selectedCount: number;\n look: TriggerLookProps;\n}) {\n if (shape === 'chips') {\n return (\n <ComboboxChips className={look.className}>\n <ComboboxValue>\n {(items: ComboboxOption[]) => (\n <>\n {items.map(item => (\n <ComboboxChip key={item.value}>{item.label}</ComboboxChip>\n ))}\n <ComboboxChipsInput\n {...controlProps}\n placeholder={items.length === 0 ? textPlaceholder(placeholder) : undefined}\n />\n </>\n )}\n </ComboboxValue>\n </ComboboxChips>\n );\n }\n\n const text = selectedCount === 0 ? placeholder : `${selectedCount} selected`;\n return (\n <ComboboxTrigger\n {...controlProps}\n variant={look.variant}\n size={look.size}\n showChevron={look.showChevron}\n className={triggerClassName(look)}\n >\n <span className={cn(selectedCount === 0 && comboboxStyles.placeholder)}>{text}</span>\n </ComboboxTrigger>\n );\n}\n\ntype ShapeProps = { shape: ComboboxShape; controlProps: ControlProps };\n\nfunction SingleCombobox({\n shape,\n controlProps,\n options,\n value,\n onValueChange,\n placeholder = 'Select option',\n searchPlaceholder = 'Search',\n emptyText = 'No option found.',\n className,\n disabled = false,\n variant = 'default',\n size = 'md',\n open,\n onOpenChange,\n container,\n align = 'start',\n showChevron = true,\n iconOnlyValue = false,\n allowCustomValue = false,\n onInputValueChange,\n}: ComboboxSingleProps & ShapeProps) {\n // The search text: a field's own text, or the compact trigger's popup search.\n const [inputValue, setInputValue] = React.useState('');\n const filter = useOptionFilter();\n const inField = shape === 'field';\n const selectedOption = options.find(option => option.value === value);\n const customOption = allowCustomValue ? getCustomOption(options, inputValue) : undefined;\n const items = customOption ? [customOption, ...options] : options;\n\n function handleInputValueChange(next: string) {\n setInputValue(next);\n onInputValueChange?.(next);\n }\n\n function handleValueChange(item: ComboboxOption | null) {\n if (!item) return;\n onValueChange?.(item.value);\n // A field shows the picked option; a compact trigger's popup search starts empty next time.\n if (!inField) handleInputValueChange('');\n }\n\n return (\n <ComboboxRoot\n autoHighlight\n items={items}\n filter={filter}\n itemToStringLabel={optionText}\n // Base UI's \"nothing selected\" is null.\n value={selectedOption ?? null}\n // A field owns its text (the selected option's, or the search); a compact trigger's search is reset here.\n inputValue={inField ? undefined : inputValue}\n onInputValueChange={handleInputValueChange}\n onValueChange={handleValueChange}\n disabled={disabled}\n open={open}\n onOpenChange={onOpenChange}\n >\n <SingleComboboxControl\n shape={shape}\n controlProps={controlProps}\n placeholder={placeholder}\n selectedOption={selectedOption}\n look={{ variant, size, showChevron, iconOnlyValue, className }}\n />\n <ComboboxOptionsContent\n showSearch={!inField}\n searchPlaceholder={searchPlaceholder}\n emptyText={emptyText}\n align={align}\n container={container}\n />\n </ComboboxRoot>\n );\n}\n\nfunction MultipleCombobox({\n shape,\n controlProps,\n options,\n value = EMPTY_VALUES,\n onValueChange,\n clearLabel,\n placeholder = 'Select options',\n searchPlaceholder = 'Search',\n emptyText = 'No option found.',\n className,\n disabled = false,\n variant = 'default',\n size = 'md',\n open,\n onOpenChange,\n container,\n align = 'start',\n showChevron = true,\n iconOnlyValue = false,\n}: ComboboxMultipleProps & ShapeProps) {\n const filter = useOptionFilter();\n const selectedValues = new Set(value);\n const selectedOptions = options.filter(option => selectedValues.has(option.value));\n const footer =\n clearLabel && selectedOptions.length > 0 ? (\n <ComboboxClearFooter label={clearLabel} onClear={() => onValueChange?.([])} />\n ) : undefined;\n\n return (\n <ComboboxRoot\n multiple\n autoHighlight\n items={options}\n filter={filter}\n value={selectedOptions}\n onValueChange={items => onValueChange?.(items.map(item => item.value))}\n disabled={disabled}\n open={open}\n onOpenChange={onOpenChange}\n >\n <MultipleComboboxControl\n shape={shape}\n controlProps={controlProps}\n placeholder={placeholder}\n selectedCount={selectedOptions.length}\n look={{ variant, size, showChevron, iconOnlyValue, className }}\n />\n <ComboboxOptionsContent\n showSearch={shape !== 'chips'}\n searchPlaceholder={searchPlaceholder}\n emptyText={emptyText}\n align={align}\n container={container}\n footer={footer}\n />\n </ComboboxRoot>\n );\n}\n\n/**\n * A searchable select driven by `options`, built on the Combobox parts.\n *\n * Inside a `Field`, a `default` combobox searches right in the field: a text field for a single value,\n * removable chips for `multiple`. Everywhere else (outside a `Field`, `ghost` and icon-size triggers,\n * custom non-text placeholders) it keeps a compact trigger with the search inside the popup. For anything\n * these props don't cover (groups, rich items), compose the parts directly (`ComboboxRoot`, ...).\n */\nexport function Combobox(props: ComboboxProps) {\n const field = useFieldAriaIds();\n const generatedName = React.useId();\n const multiple = isMultipleCombobox(props);\n const shape = getComboboxShape({\n inFieldContext: Boolean(field),\n multiple,\n variant: props.variant ?? 'default',\n size: props.size ?? 'md',\n iconOnlyValue: props.iconOnlyValue ?? false,\n showChevron: props.showChevron ?? true,\n placeholder: props.placeholder,\n });\n const errorName = props.name ?? generatedName;\n const controlProps = getControlProps({\n id: props.id,\n ariaLabel: props['aria-label'],\n ariaDescribedBy: props['aria-describedby'],\n hasLabel: Boolean(props.id || field),\n multiple,\n error: props.error,\n errorName,\n });\n\n const root = isMultipleCombobox(props) ? (\n <MultipleCombobox {...props} shape={shape} controlProps={controlProps} />\n ) : (\n <SingleCombobox {...props} shape={shape} controlProps={controlProps} />\n );\n\n if (!props.error) return root;\n\n return (\n <div className={comboboxStyles.root}>\n {root}\n <FieldBlock.ErrorMsg name={errorName}>{props.error}</FieldBlock.ErrorMsg>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ const require_utils = require("./utils-uDc1ofX1.cjs");
|
|
|
3
3
|
const require_primitives_form_element = require("./primitives/form-element.cjs.js");
|
|
4
4
|
const require_Button = require("./Button-DLcMcuUL.cjs");
|
|
5
5
|
const require_field_control_aria = require("./field-control-aria-Dw0_ObOU.cjs");
|
|
6
|
-
const require_combobox_parts = require("./combobox-parts-
|
|
6
|
+
const require_combobox_parts = require("./combobox-parts-C4phOq4t.cjs");
|
|
7
7
|
const require_field_error_id = require("./field-error-id-nGAF0330.cjs");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
react = require_rolldown_runtime.__toESM(react, 1);
|
|
@@ -161,7 +161,7 @@ function MultipleComboboxControl({ shape, controlProps, placeholder, selectedCou
|
|
|
161
161
|
})
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
-
function SingleCombobox({ shape, controlProps, options, value, onValueChange, placeholder = "Select option
|
|
164
|
+
function SingleCombobox({ shape, controlProps, options, value, onValueChange, placeholder = "Select option", searchPlaceholder = "Search", emptyText = "No option found.", className, disabled = false, variant = "default", size = "md", open, onOpenChange, container, align = "start", showChevron = true, iconOnlyValue = false, allowCustomValue = false, onInputValueChange }) {
|
|
165
165
|
const [inputValue, setInputValue] = react.useState("");
|
|
166
166
|
const filter = useOptionFilter();
|
|
167
167
|
const inField = shape === "field";
|
|
@@ -210,7 +210,7 @@ function SingleCombobox({ shape, controlProps, options, value, onValueChange, pl
|
|
|
210
210
|
})]
|
|
211
211
|
});
|
|
212
212
|
}
|
|
213
|
-
function MultipleCombobox({ shape, controlProps, options, value = EMPTY_VALUES, onValueChange, clearLabel, placeholder = "Select options
|
|
213
|
+
function MultipleCombobox({ shape, controlProps, options, value = EMPTY_VALUES, onValueChange, clearLabel, placeholder = "Select options", searchPlaceholder = "Search", emptyText = "No option found.", className, disabled = false, variant = "default", size = "md", open, onOpenChange, container, align = "start", showChevron = true, iconOnlyValue = false }) {
|
|
214
214
|
const filter = useOptionFilter();
|
|
215
215
|
const selectedValues = new Set(value);
|
|
216
216
|
const selectedOptions = options.filter((option) => selectedValues.has(option.value));
|
|
@@ -306,4 +306,4 @@ Object.defineProperty(exports, "Combobox", {
|
|
|
306
306
|
}
|
|
307
307
|
});
|
|
308
308
|
|
|
309
|
-
//# sourceMappingURL=combobox-
|
|
309
|
+
//# sourceMappingURL=combobox-DIT_6xiG.cjs.map
|