@dreamtree-org/twreact-ui 1.3.1 → 1.3.3
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/DESIGN.md +2 -2
- package/README.md +90 -5
- package/ai-skills/dreamtree-ui.md +43 -4
- package/bin/cli.mjs +72 -4
- package/charts/package.json +5 -0
- package/dist/_shared/cn-DPPQUJu1.js +138 -0
- package/dist/ai-schema.json +639 -1
- package/dist/charts/index.d.ts +9 -0
- package/dist/charts/index.d.ts.map +1 -0
- package/dist/charts/index.js +797 -0
- package/dist/components/charts/AreaChart.d.ts +4 -0
- package/dist/components/charts/AreaChart.d.ts.map +1 -0
- package/dist/components/charts/BarChart.d.ts +4 -0
- package/dist/components/charts/BarChart.d.ts.map +1 -0
- package/dist/components/charts/CartesianChart.d.ts +5 -0
- package/dist/components/charts/CartesianChart.d.ts.map +1 -0
- package/dist/components/charts/ChartFrame.d.ts +18 -0
- package/dist/components/charts/ChartFrame.d.ts.map +1 -0
- package/dist/components/charts/DonutChart.d.ts +4 -0
- package/dist/components/charts/DonutChart.d.ts.map +1 -0
- package/dist/components/charts/LineChart.d.ts +4 -0
- package/dist/components/charts/LineChart.d.ts.map +1 -0
- package/dist/components/charts/Sparkline.d.ts +5 -0
- package/dist/components/charts/Sparkline.d.ts.map +1 -0
- package/dist/components/charts/chartTheme.d.ts +36 -0
- package/dist/components/charts/chartTheme.d.ts.map +1 -0
- package/dist/components/core/Table/MobileCard.d.ts.map +1 -1
- package/dist/components/core/Table/MobileFilters.d.ts.map +1 -1
- package/dist/components/core/Table/Table.d.ts.map +1 -1
- package/dist/components/rich-text/RichTextEditor.d.ts +4 -0
- package/dist/components/rich-text/RichTextEditor.d.ts.map +1 -0
- package/dist/components/rich-text/RichTextToolbar.d.ts +219 -0
- package/dist/components/rich-text/RichTextToolbar.d.ts.map +1 -0
- package/dist/components/utility/Stepper.d.ts.map +1 -1
- package/dist/esm/charts/index.js +5 -0
- package/dist/esm/components/blocks/StatCard.js +1 -1
- package/dist/esm/components/charts/AreaChart.js +16 -0
- package/dist/esm/components/charts/BarChart.js +16 -0
- package/dist/esm/components/charts/CartesianChart.js +255 -0
- package/dist/esm/components/charts/ChartFrame.js +170 -0
- package/dist/esm/components/charts/DonutChart.js +122 -0
- package/dist/esm/components/charts/LineChart.js +16 -0
- package/dist/esm/components/charts/Sparkline.js +113 -0
- package/dist/esm/components/charts/chartTheme.js +126 -0
- package/dist/esm/components/core/Select.js +2 -2
- package/dist/esm/components/core/Table/MobileCard.js +5 -2
- package/dist/esm/components/core/Table/MobileFilters.js +10 -3
- package/dist/esm/components/core/Table/Table.js +10 -7
- package/dist/esm/components/rich-text/RichTextEditor.js +244 -0
- package/dist/esm/components/rich-text/RichTextToolbar.js +397 -0
- package/dist/esm/components/utility/Stepper.js +6 -4
- package/dist/esm/rich-text/index.js +1 -0
- package/dist/index.css +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +50 -22
- package/dist/index.js +50 -22
- package/dist/rich-text/index.d.ts +5 -0
- package/dist/rich-text/index.d.ts.map +1 -0
- package/dist/rich-text/index.js +654 -0
- package/dist/tokens/brands/practicepro.json +1 -0
- package/dist/tokens/brands/studio.css +14 -0
- package/dist/tokens/brands/studio.json +21 -0
- package/dist/types/charts.d.ts +97 -0
- package/dist/types/charts.d.ts.map +1 -0
- package/dist/types/rich-text.d.ts +50 -0
- package/dist/types/rich-text.d.ts.map +1 -0
- package/doc/README.md +13 -0
- package/mcp/catalog.snapshot.json +1016 -10
- package/mcp/server-core.mjs +30 -1
- package/package.json +39 -2
- package/rich-text/package.json +5 -0
- package/tokens/brand.schema.json +612 -0
- package/tokens/brands/practicepro.json +1 -0
- package/tokens/brands/studio.json +21 -0
- package/tokens/lib.cjs +131 -7
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
|
+
import { useSyncExternalStore, useId } from 'react';
|
|
4
|
+
import { prefersReducedMotion } from '../../utils/position.js';
|
|
5
|
+
|
|
6
|
+
var CHART_PALETTES = ["primary", "secondary", "success", "warning", "error", "info", "gray"];
|
|
7
|
+
var DEFAULT_CHART_COLORS = ["primary", "success", "warning", "error", "info", "secondary", "gray"];
|
|
8
|
+
var TOKEN_RE = new RegExp("^(".concat(CHART_PALETTES.join("|"), ")(?:-(50|[1-9]00|950))?$"));
|
|
9
|
+
var CHROME_VARS = {
|
|
10
|
+
grid: "--dt-line",
|
|
11
|
+
axis: "--dt-muted",
|
|
12
|
+
surface: "--dt-surface",
|
|
13
|
+
text: "--dt-foreground"
|
|
14
|
+
};
|
|
15
|
+
/** `"primary"` → `"--dt-primary-500"`, `"error-300"` → `"--dt-error-300"`; null for anything else. */
|
|
16
|
+
function tokenVar(color) {
|
|
17
|
+
var m = TOKEN_RE.exec(String(color || ""));
|
|
18
|
+
return m ? "--dt-".concat(m[1], "-").concat(m[2] || "500") : null;
|
|
19
|
+
}
|
|
20
|
+
function readVar(name) {
|
|
21
|
+
if (typeof window === "undefined" || typeof window.getComputedStyle !== "function") return "";
|
|
22
|
+
return window.getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
23
|
+
}
|
|
24
|
+
// SVG presentation attributes do not resolve var() in every engine, so a token is
|
|
25
|
+
// turned into a concrete rgb() string when the browser can tell us its value. The
|
|
26
|
+
// var() form is the SSR / jsdom fallback, and what the first (hydrating) client
|
|
27
|
+
// render uses so it matches the server's HTML (`live` = false).
|
|
28
|
+
function resolveVar(name) {
|
|
29
|
+
var live = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
30
|
+
var value = live ? readVar(name) : "";
|
|
31
|
+
return value ? "rgb(".concat(value.split(/[\s,]+/).join(" "), ")") : "rgb(var(".concat(name, "))");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A series colour: a palette token (`primary`, `success-600`, …) resolves to the
|
|
35
|
+
* matching `--dt-*` variable; any other string is treated as a CSS colour supplied
|
|
36
|
+
* by the consumer (e.g. a colour stored with the data).
|
|
37
|
+
*/
|
|
38
|
+
function resolveColor(color) {
|
|
39
|
+
var live = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
40
|
+
var name = tokenVar(color);
|
|
41
|
+
return name ? resolveVar(name, live) : color;
|
|
42
|
+
}
|
|
43
|
+
function seriesColors(categories, colors) {
|
|
44
|
+
var live = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
45
|
+
var palette = colors && colors.length ? colors : DEFAULT_CHART_COLORS;
|
|
46
|
+
return categories.map(function (_, i) {
|
|
47
|
+
return resolveColor(palette[i % palette.length], live);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function readChrome(live) {
|
|
51
|
+
return Object.fromEntries(Object.entries(CHROME_VARS).map(function (_ref) {
|
|
52
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
53
|
+
k = _ref2[0],
|
|
54
|
+
v = _ref2[1];
|
|
55
|
+
return [k, resolveVar(v, live)];
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
// The theme is an external store: the attributes on <html> that flip it. The
|
|
59
|
+
// server snapshot is null, so a hydrating render keeps the var() fallback the
|
|
60
|
+
// server rendered and React re-renders with live colours right after (#130).
|
|
61
|
+
function subscribeTheme(onChange) {
|
|
62
|
+
if (typeof document === "undefined" || typeof MutationObserver !== "function") return function () {};
|
|
63
|
+
var observer = new MutationObserver(onChange);
|
|
64
|
+
observer.observe(document.documentElement, {
|
|
65
|
+
attributes: true,
|
|
66
|
+
attributeFilter: ["class", "data-theme", "style"]
|
|
67
|
+
});
|
|
68
|
+
return function () {
|
|
69
|
+
return observer.disconnect();
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function themeSnapshot() {
|
|
73
|
+
if (typeof document === "undefined") return null;
|
|
74
|
+
var el = document.documentElement;
|
|
75
|
+
return "".concat(el.className, "|").concat(el.getAttribute("data-theme") || "", "|").concat(el.getAttribute("style") || "");
|
|
76
|
+
}
|
|
77
|
+
var serverThemeSnapshot = function serverThemeSnapshot() {
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Chart chrome + series colours, re-read whenever the theme flips (the `dark`
|
|
82
|
+
* class or `data-theme` on <html>) so axes and series follow dark mode without
|
|
83
|
+
* a remount.
|
|
84
|
+
*/
|
|
85
|
+
function useChartTheme(categories, colors) {
|
|
86
|
+
// The snapshot only signals a change; the values come from the live document.
|
|
87
|
+
var live = useSyncExternalStore(subscribeTheme, themeSnapshot, serverThemeSnapshot) !== null;
|
|
88
|
+
return {
|
|
89
|
+
chrome: readChrome(live),
|
|
90
|
+
series: seriesColors(categories, colors, live)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function subscribeReducedMotion(onChange) {
|
|
94
|
+
var _query$addEventListen;
|
|
95
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return function () {};
|
|
96
|
+
var query = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
97
|
+
(_query$addEventListen = query.addEventListener) === null || _query$addEventListen === void 0 || _query$addEventListen.call(query, "change", onChange);
|
|
98
|
+
return function () {
|
|
99
|
+
var _query$removeEventLis;
|
|
100
|
+
return (_query$removeEventLis = query.removeEventListener) === null || _query$removeEventLis === void 0 ? void 0 : _query$removeEventLis.call(query, "change", onChange);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
var serverReducedMotion = function serverReducedMotion() {
|
|
104
|
+
return false;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* `true` while the user asks the OS for reduced motion; follows changes live.
|
|
108
|
+
* `false` on the server and while hydrating, so the first render matches.
|
|
109
|
+
*/
|
|
110
|
+
function useReducedMotion() {
|
|
111
|
+
return useSyncExternalStore(subscribeReducedMotion, prefersReducedMotion, serverReducedMotion);
|
|
112
|
+
}
|
|
113
|
+
var defaultValueFormatter = function defaultValueFormatter(value) {
|
|
114
|
+
return typeof value === "number" ? value.toLocaleString() : value == null ? "" : String(value);
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* A per-instance id for the SVG defs recharts draws (clip paths). recharts 2
|
|
118
|
+
* falls back to a module-level counter, which differs between the server and the
|
|
119
|
+
* client; an id from React.useId() is the same on both. Only [A-Za-z0-9_-] so it
|
|
120
|
+
* is safe inside `url(#…)`.
|
|
121
|
+
*/
|
|
122
|
+
function useChartId() {
|
|
123
|
+
return "dt-chart-".concat(useId().replace(/[^A-Za-z0-9_-]/g, ""));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { CHART_PALETTES, DEFAULT_CHART_COLORS, defaultValueFormatter, resolveColor, seriesColors, tokenVar, useChartId, useChartTheme, useReducedMotion };
|
|
@@ -127,7 +127,7 @@ var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
127
127
|
onMenuItemRender = _ref.onMenuItemRender,
|
|
128
128
|
className = _ref.className,
|
|
129
129
|
_ref$allowClear = _ref.allowClear,
|
|
130
|
-
allowClear = _ref$allowClear === void 0 ?
|
|
130
|
+
allowClear = _ref$allowClear === void 0 ? false : _ref$allowClear,
|
|
131
131
|
_ref$creatable = _ref.creatable,
|
|
132
132
|
creatable = _ref$creatable === void 0 ? false : _ref$creatable,
|
|
133
133
|
onCreateOption = _ref.onCreateOption,
|
|
@@ -564,7 +564,7 @@ var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
|
|
|
564
564
|
}, opt.value);
|
|
565
565
|
})
|
|
566
566
|
}), jsxs("div", {
|
|
567
|
-
className: "text-xs text-primary-
|
|
567
|
+
className: "text-xs text-primary-700",
|
|
568
568
|
children: ["+", rest]
|
|
569
569
|
})]
|
|
570
570
|
});
|
|
@@ -128,6 +128,7 @@ function MobileCard(_ref) {
|
|
|
128
128
|
}), menuActions.length > 0 && jsxs("div", {
|
|
129
129
|
className: "relative",
|
|
130
130
|
children: [jsx("button", {
|
|
131
|
+
type: "button",
|
|
131
132
|
ref: function ref(el) {
|
|
132
133
|
return registerActionButton === null || registerActionButton === void 0 ? void 0 : registerActionButton(actionCellKey, el);
|
|
133
134
|
},
|
|
@@ -137,8 +138,10 @@ function MobileCard(_ref) {
|
|
|
137
138
|
"aria-expanded": activeActionRowKey === actionCellKey,
|
|
138
139
|
className: "p-1.5 rounded hover:bg-gray-200",
|
|
139
140
|
title: "Actions",
|
|
141
|
+
"aria-label": "Actions",
|
|
140
142
|
children: jsx(MoreVertical, {
|
|
141
|
-
className: "h-4 w-4"
|
|
143
|
+
className: "h-4 w-4",
|
|
144
|
+
"aria-hidden": "true"
|
|
142
145
|
})
|
|
143
146
|
}), activeActionRowKey === actionCellKey && jsx(ActionMenu, {
|
|
144
147
|
actions: menuActions,
|
|
@@ -166,7 +169,7 @@ function MobileCard(_ref) {
|
|
|
166
169
|
return jsxs("div", {
|
|
167
170
|
className: cn("flex justify-between items-center flex-row py-2 border-b last:border-b-0", safeExtraCellClass),
|
|
168
171
|
children: [jsx("div", {
|
|
169
|
-
className: "text-xs font-medium text-primary-
|
|
172
|
+
className: "text-xs font-medium text-primary-700 uppercase tracking-wide mb-1 sm:mb-0 sm:w-1/3 sm:pr-2",
|
|
170
173
|
children: column.label
|
|
171
174
|
}), jsx("div", {
|
|
172
175
|
className: "text-sm text-primary-900 sm:w-2/3 sm:pl-2 break-words",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import 'react';
|
|
3
|
+
import React from 'react';
|
|
4
4
|
import { Search } from 'lucide-react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -17,6 +17,7 @@ function MobileFilters(_ref) {
|
|
|
17
17
|
visibleColumns = _ref.visibleColumns,
|
|
18
18
|
filters = _ref.filters,
|
|
19
19
|
handleFilter = _ref.handleFilter;
|
|
20
|
+
var idPrefix = React.useId();
|
|
20
21
|
return jsxs("div", {
|
|
21
22
|
className: "mb-4 p-3 bg-gray-50 rounded-lg",
|
|
22
23
|
children: [jsxs("div", {
|
|
@@ -25,12 +26,14 @@ function MobileFilters(_ref) {
|
|
|
25
26
|
className: "text-sm font-medium",
|
|
26
27
|
children: "Filters"
|
|
27
28
|
}), jsx("button", {
|
|
29
|
+
type: "button",
|
|
28
30
|
onClick: function onClick() {
|
|
29
31
|
return setShowMobileFilters(function (s) {
|
|
30
32
|
return !s;
|
|
31
33
|
});
|
|
32
34
|
},
|
|
33
|
-
|
|
35
|
+
"aria-expanded": !!showMobileFilters,
|
|
36
|
+
className: "text-xs font-medium text-primary-700",
|
|
34
37
|
children: showMobileFilters ? "Hide" : "Show"
|
|
35
38
|
})]
|
|
36
39
|
}), showMobileFilters && jsxs("div", {
|
|
@@ -40,6 +43,7 @@ function MobileFilters(_ref) {
|
|
|
40
43
|
children: [jsx("input", {
|
|
41
44
|
type: "text",
|
|
42
45
|
placeholder: "Search...",
|
|
46
|
+
"aria-label": "Search",
|
|
43
47
|
className: "w-full rounded-md border border-gray-300 px-4 py-2 text-sm",
|
|
44
48
|
value: searchInput,
|
|
45
49
|
onChange: function onChange(e) {
|
|
@@ -47,14 +51,17 @@ function MobileFilters(_ref) {
|
|
|
47
51
|
},
|
|
48
52
|
onKeyDown: onGlobalKeyDown
|
|
49
53
|
}), jsx(Search, {
|
|
50
|
-
className: "absolute right-3 top-2.5 h-4 w-4 text-primary-400"
|
|
54
|
+
className: "absolute right-3 top-2.5 h-4 w-4 text-primary-400",
|
|
55
|
+
"aria-hidden": "true"
|
|
51
56
|
})]
|
|
52
57
|
}), filterable && visibleColumns.map(function (column) {
|
|
53
58
|
return jsxs("div", {
|
|
54
59
|
children: [jsx("label", {
|
|
60
|
+
htmlFor: "".concat(idPrefix, "-filter-").concat(column.key),
|
|
55
61
|
className: "block text-xs font-medium mb-1",
|
|
56
62
|
children: column.label
|
|
57
63
|
}), jsx("input", {
|
|
64
|
+
id: "".concat(idPrefix, "-filter-").concat(column.key),
|
|
58
65
|
type: "text",
|
|
59
66
|
placeholder: "Filter ".concat(column.label),
|
|
60
67
|
className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
|
|
@@ -1179,10 +1179,13 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1179
1179
|
children: [showReloadButton && jsx(Fragment, {
|
|
1180
1180
|
children: renderReloadButton ? typeof renderReloadButton === "function" ? renderReloadButton() : renderReloadButton : jsxs("button", {
|
|
1181
1181
|
type: "button",
|
|
1182
|
+
"aria-label": "Reload",
|
|
1183
|
+
title: "Reload",
|
|
1182
1184
|
className: "px-4 py-2 bg-primary-100 flex items-center gap-2 rounded-lg md:rounded text-primary-700 hover:bg-primary-200 transition-colors",
|
|
1183
1185
|
onClick: onReload,
|
|
1184
1186
|
children: [jsx(RefreshCcw, {
|
|
1185
|
-
className: "size-4"
|
|
1187
|
+
className: "size-4",
|
|
1188
|
+
"aria-hidden": "true"
|
|
1186
1189
|
}), jsx("span", {
|
|
1187
1190
|
className: "hidden sm:inline",
|
|
1188
1191
|
children: "Reload"
|
|
@@ -1323,7 +1326,7 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1323
1326
|
children: [hasDetailsColumn && jsx("th", {
|
|
1324
1327
|
className: "w-8 px-4 py-3"
|
|
1325
1328
|
}), showSerial && jsx("th", {
|
|
1326
|
-
className: "w-8 px-4 py-3 text-left text-xs font-medium text-primary-
|
|
1329
|
+
className: "w-8 px-4 py-3 text-left text-xs font-medium text-primary-700 uppercase tracking-wider",
|
|
1327
1330
|
children: "#"
|
|
1328
1331
|
}), selectable && jsx("th", {
|
|
1329
1332
|
className: "w-12 px-6 py-3",
|
|
@@ -1336,7 +1339,7 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1336
1339
|
}), visibleColumns.map(function (column) {
|
|
1337
1340
|
var _column$rules;
|
|
1338
1341
|
return jsx("th", {
|
|
1339
|
-
className: cn("px-6 py-3 text-left text-xs font-medium text-primary-
|
|
1342
|
+
className: cn("px-6 py-3 text-left text-xs font-medium text-primary-700 uppercase tracking-wider", {
|
|
1340
1343
|
"cursor-pointer hover:bg-gray-100": sortable && column.sortable !== false
|
|
1341
1344
|
}),
|
|
1342
1345
|
onClick: function onClick() {
|
|
@@ -1427,7 +1430,7 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1427
1430
|
children: [jsx("div", {
|
|
1428
1431
|
className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
|
|
1429
1432
|
}), jsx("span", {
|
|
1430
|
-
className: "text-sm text-
|
|
1433
|
+
className: "text-sm text-muted",
|
|
1431
1434
|
children: "Loading..."
|
|
1432
1435
|
})]
|
|
1433
1436
|
})
|
|
@@ -1445,7 +1448,7 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1445
1448
|
className: "h-8 w-8"
|
|
1446
1449
|
})
|
|
1447
1450
|
}), jsx("span", {
|
|
1448
|
-
className: "text-sm text-
|
|
1451
|
+
className: "text-sm text-muted",
|
|
1449
1452
|
children: "No results found."
|
|
1450
1453
|
})]
|
|
1451
1454
|
})
|
|
@@ -1490,7 +1493,7 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1490
1493
|
children: [jsx("div", {
|
|
1491
1494
|
className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
|
|
1492
1495
|
}), jsx("span", {
|
|
1493
|
-
className: "text-sm text-
|
|
1496
|
+
className: "text-sm text-muted",
|
|
1494
1497
|
children: "Loading..."
|
|
1495
1498
|
})]
|
|
1496
1499
|
})
|
|
@@ -1504,7 +1507,7 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
1504
1507
|
className: "h-8 w-8"
|
|
1505
1508
|
})
|
|
1506
1509
|
}), jsx("span", {
|
|
1507
|
-
className: "text-sm text-
|
|
1510
|
+
className: "text-sm text-muted",
|
|
1508
1511
|
children: "No results found."
|
|
1509
1512
|
})]
|
|
1510
1513
|
})
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2, toConsumableArray as _toConsumableArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { forwardRef, useId, useRef, useEffect, useImperativeHandle } from 'react';
|
|
5
|
+
import { useEditor, useEditorState, EditorContent } from '@tiptap/react';
|
|
6
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
7
|
+
import { AlertCircle } from 'lucide-react';
|
|
8
|
+
import { cn } from '../../utils/cn.js';
|
|
9
|
+
import { useLatestRef } from '../../hooks/useLatestRef.js';
|
|
10
|
+
import { selectToolbarState, DEFAULT_TOOLBAR, RichTextToolbar } from './RichTextToolbar.js';
|
|
11
|
+
|
|
12
|
+
var _excluded = ["id", "className", "editorClassName", "label", "error", "helperText", "required", "disabled", "readOnly", "placeholder", "value", "defaultValue", "onChange", "outputFormat", "onFocus", "onBlur", "toolbar", "toolbarLabel", "minHeight", "maxHeight", "autoFocus", "extensions", "aria-label", "aria-describedby"];
|
|
13
|
+
// Tailwind's preflight strips list bullets, heading sizes, etc. These arbitrary
|
|
14
|
+
// variants restore readable defaults inside the editable area only.
|
|
15
|
+
var CONTENT_CLASSES = cn("px-3 py-2 text-sm text-gray-900 focus:outline-none dark:text-gray-100", "[&_p]:my-1 [&_h1]:my-2 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:my-2 [&_h2]:text-xl [&_h2]:font-semibold [&_h3]:my-1.5 [&_h3]:text-lg [&_h3]:font-semibold", "[&_ul]:list-disc [&_ul]:pl-6 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:my-0.5", "[&_blockquote]:my-2 [&_blockquote]:border-l-4 [&_blockquote]:border-gray-300 [&_blockquote]:pl-3 [&_blockquote]:text-gray-600 dark:[&_blockquote]:border-gray-600 dark:[&_blockquote]:text-gray-300", "[&_code]:rounded [&_code]:bg-gray-100 [&_code]:px-1 [&_code]:font-mono [&_code]:text-[0.85em] dark:[&_code]:bg-gray-800", "[&_pre]:my-2 [&_pre]:rounded-md [&_pre]:bg-gray-100 [&_pre]:p-3 dark:[&_pre]:bg-gray-800 [&_pre_code]:bg-transparent [&_pre_code]:p-0", "[&_a]:text-primary-600 [&_a]:underline dark:[&_a]:text-primary-400 [&_hr]:my-3 [&_hr]:border-gray-200 dark:[&_hr]:border-gray-700");
|
|
16
|
+
var toCss = function toCss(v) {
|
|
17
|
+
return typeof v === "number" ? "".concat(v, "px") : v;
|
|
18
|
+
};
|
|
19
|
+
function sameContent(editor, value) {
|
|
20
|
+
if (typeof value === "string") return value === editor.getHTML();
|
|
21
|
+
return JSON.stringify(value) === JSON.stringify(editor.getJSON());
|
|
22
|
+
}
|
|
23
|
+
var RichTextEditor = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
|
24
|
+
var _ref2;
|
|
25
|
+
var id = _ref.id,
|
|
26
|
+
className = _ref.className,
|
|
27
|
+
editorClassName = _ref.editorClassName,
|
|
28
|
+
label = _ref.label,
|
|
29
|
+
error = _ref.error,
|
|
30
|
+
helperText = _ref.helperText,
|
|
31
|
+
_ref$required = _ref.required,
|
|
32
|
+
required = _ref$required === void 0 ? false : _ref$required,
|
|
33
|
+
_ref$disabled = _ref.disabled,
|
|
34
|
+
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
35
|
+
_ref$readOnly = _ref.readOnly,
|
|
36
|
+
readOnly = _ref$readOnly === void 0 ? false : _ref$readOnly,
|
|
37
|
+
placeholder = _ref.placeholder,
|
|
38
|
+
value = _ref.value,
|
|
39
|
+
defaultValue = _ref.defaultValue,
|
|
40
|
+
onChange = _ref.onChange,
|
|
41
|
+
_ref$outputFormat = _ref.outputFormat,
|
|
42
|
+
outputFormat = _ref$outputFormat === void 0 ? "html" : _ref$outputFormat,
|
|
43
|
+
onFocus = _ref.onFocus,
|
|
44
|
+
onBlur = _ref.onBlur,
|
|
45
|
+
_ref$toolbar = _ref.toolbar,
|
|
46
|
+
toolbar = _ref$toolbar === void 0 ? DEFAULT_TOOLBAR : _ref$toolbar,
|
|
47
|
+
_ref$toolbarLabel = _ref.toolbarLabel,
|
|
48
|
+
toolbarLabel = _ref$toolbarLabel === void 0 ? "Formatting" : _ref$toolbarLabel,
|
|
49
|
+
_ref$minHeight = _ref.minHeight,
|
|
50
|
+
minHeight = _ref$minHeight === void 0 ? 120 : _ref$minHeight,
|
|
51
|
+
maxHeight = _ref.maxHeight,
|
|
52
|
+
_ref$autoFocus = _ref.autoFocus,
|
|
53
|
+
autoFocus = _ref$autoFocus === void 0 ? false : _ref$autoFocus,
|
|
54
|
+
extensions = _ref.extensions,
|
|
55
|
+
ariaLabel = _ref["aria-label"],
|
|
56
|
+
ariaDescribedBy = _ref["aria-describedby"],
|
|
57
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
58
|
+
var reactId = useId();
|
|
59
|
+
var editorId = id || "rich-text-".concat(reactId);
|
|
60
|
+
var labelId = "".concat(editorId, "-label");
|
|
61
|
+
var errorId = "".concat(editorId, "-error");
|
|
62
|
+
var helperId = "".concat(editorId, "-helper");
|
|
63
|
+
var editable = !disabled && !readOnly;
|
|
64
|
+
var rootRef = useRef(null);
|
|
65
|
+
var callbacks = useLatestRef({
|
|
66
|
+
onChange: onChange,
|
|
67
|
+
onFocus: onFocus,
|
|
68
|
+
onBlur: onBlur,
|
|
69
|
+
outputFormat: outputFormat
|
|
70
|
+
});
|
|
71
|
+
var describedBy = [error ? errorId : null, helperText ? helperId : null, ariaDescribedBy].filter(Boolean).join(" ") || undefined;
|
|
72
|
+
var attributes = _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
|
|
73
|
+
id: editorId,
|
|
74
|
+
role: "textbox",
|
|
75
|
+
"aria-multiline": "true"
|
|
76
|
+
}, label ? {
|
|
77
|
+
"aria-labelledby": labelId
|
|
78
|
+
} : {
|
|
79
|
+
"aria-label": ariaLabel || placeholder || "Rich text editor"
|
|
80
|
+
}), describedBy ? {
|
|
81
|
+
"aria-describedby": describedBy
|
|
82
|
+
} : {}), error ? {
|
|
83
|
+
"aria-invalid": "true"
|
|
84
|
+
} : {}), required ? {
|
|
85
|
+
"aria-required": "true"
|
|
86
|
+
} : {}), readOnly ? {
|
|
87
|
+
"aria-readonly": "true"
|
|
88
|
+
} : {}), disabled ? {
|
|
89
|
+
"aria-disabled": "true"
|
|
90
|
+
} : {}), placeholder ? {
|
|
91
|
+
"aria-placeholder": placeholder
|
|
92
|
+
} : {}), {}, {
|
|
93
|
+
"class": cn(CONTENT_CLASSES, editorClassName)
|
|
94
|
+
});
|
|
95
|
+
var attributesKey = JSON.stringify(attributes);
|
|
96
|
+
var editor = useEditor({
|
|
97
|
+
extensions: [StarterKit.configure({
|
|
98
|
+
link: {
|
|
99
|
+
openOnClick: false,
|
|
100
|
+
autolink: true,
|
|
101
|
+
defaultProtocol: "https"
|
|
102
|
+
}
|
|
103
|
+
})].concat(_toConsumableArray(extensions || [])),
|
|
104
|
+
content: (_ref2 = value !== null && value !== void 0 ? value : defaultValue) !== null && _ref2 !== void 0 ? _ref2 : "",
|
|
105
|
+
editable: editable,
|
|
106
|
+
autofocus: autoFocus,
|
|
107
|
+
// Render on the client after mount: safe for SSR / Next.js hydration.
|
|
108
|
+
immediatelyRender: false,
|
|
109
|
+
editorProps: {
|
|
110
|
+
attributes: attributes
|
|
111
|
+
},
|
|
112
|
+
onUpdate: function onUpdate(_ref3) {
|
|
113
|
+
var instance = _ref3.editor;
|
|
114
|
+
var _callbacks$current = callbacks.current,
|
|
115
|
+
cb = _callbacks$current.onChange,
|
|
116
|
+
format = _callbacks$current.outputFormat;
|
|
117
|
+
cb === null || cb === void 0 || cb(format === "json" ? instance.getJSON() : instance.getHTML(), instance);
|
|
118
|
+
},
|
|
119
|
+
onFocus: function onFocus(_ref4) {
|
|
120
|
+
var _callbacks$current$on, _callbacks$current2;
|
|
121
|
+
var event = _ref4.event;
|
|
122
|
+
return (_callbacks$current$on = (_callbacks$current2 = callbacks.current).onFocus) === null || _callbacks$current$on === void 0 ? void 0 : _callbacks$current$on.call(_callbacks$current2, event);
|
|
123
|
+
},
|
|
124
|
+
onBlur: function onBlur(_ref5) {
|
|
125
|
+
var _callbacks$current$on2, _callbacks$current3;
|
|
126
|
+
var event = _ref5.event;
|
|
127
|
+
return (_callbacks$current$on2 = (_callbacks$current3 = callbacks.current).onBlur) === null || _callbacks$current$on2 === void 0 ? void 0 : _callbacks$current$on2.call(_callbacks$current3, event);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
var watched = useEditorState({
|
|
131
|
+
editor: editor,
|
|
132
|
+
selector: function selector(_ref6) {
|
|
133
|
+
var instance = _ref6.editor;
|
|
134
|
+
return selectToolbarState(instance);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// useEditorState only re-selects on the next transaction after the editor
|
|
138
|
+
// mounts, so read the state directly until then.
|
|
139
|
+
var state = watched !== null && watched !== void 0 ? watched : selectToolbarState(editor);
|
|
140
|
+
useEffect(function () {
|
|
141
|
+
if (editor && value !== undefined && value !== null && !sameContent(editor, value)) {
|
|
142
|
+
editor.commands.setContent(value, {
|
|
143
|
+
emitUpdate: false
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}, [editor, value]);
|
|
147
|
+
useEffect(function () {
|
|
148
|
+
if (editor && editor.isEditable !== editable) editor.setEditable(editable, false);
|
|
149
|
+
}, [editor, editable]);
|
|
150
|
+
useEffect(function () {
|
|
151
|
+
if (editor) editor.setOptions({
|
|
152
|
+
editorProps: {
|
|
153
|
+
attributes: JSON.parse(attributesKey)
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}, [editor, attributesKey]);
|
|
157
|
+
useImperativeHandle(ref, function () {
|
|
158
|
+
return {
|
|
159
|
+
editor: editor,
|
|
160
|
+
element: rootRef.current,
|
|
161
|
+
focus: function focus() {
|
|
162
|
+
return editor === null || editor === void 0 ? void 0 : editor.commands.focus();
|
|
163
|
+
},
|
|
164
|
+
clear: function clear() {
|
|
165
|
+
return editor === null || editor === void 0 ? void 0 : editor.commands.clearContent(true);
|
|
166
|
+
},
|
|
167
|
+
getHTML: function getHTML() {
|
|
168
|
+
return editor ? editor.getHTML() : "";
|
|
169
|
+
},
|
|
170
|
+
getJSON: function getJSON() {
|
|
171
|
+
return editor ? editor.getJSON() : null;
|
|
172
|
+
},
|
|
173
|
+
setContent: function setContent(content) {
|
|
174
|
+
return editor === null || editor === void 0 ? void 0 : editor.commands.setContent(content);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}, [editor]);
|
|
178
|
+
var focusEditor = function focusEditor() {
|
|
179
|
+
if (editable) editor === null || editor === void 0 || editor.commands.focus();
|
|
180
|
+
};
|
|
181
|
+
var items = Array.isArray(toolbar) ? toolbar : toolbar === false ? [] : DEFAULT_TOOLBAR;
|
|
182
|
+
var showToolbar = !readOnly && items.length > 0;
|
|
183
|
+
var showPlaceholder = placeholder && (state ? state.isEmpty : !(value !== null && value !== void 0 ? value : defaultValue));
|
|
184
|
+
return jsxs("div", _objectSpread2(_objectSpread2({
|
|
185
|
+
ref: rootRef,
|
|
186
|
+
className: cn("w-full", className),
|
|
187
|
+
"data-disabled": disabled || undefined
|
|
188
|
+
}, rest), {}, {
|
|
189
|
+
children: [label && jsxs("label", {
|
|
190
|
+
id: labelId,
|
|
191
|
+
onClick: focusEditor,
|
|
192
|
+
className: "mb-1 block text-sm font-medium text-gray-700 dark:text-gray-200",
|
|
193
|
+
children: [label, required && jsx("span", {
|
|
194
|
+
className: "ml-1 text-error-500",
|
|
195
|
+
"aria-hidden": "true",
|
|
196
|
+
children: "*"
|
|
197
|
+
})]
|
|
198
|
+
}), jsxs("div", {
|
|
199
|
+
className: cn("overflow-hidden rounded-md border bg-white dark:bg-gray-900", error ? "border-error-500" : "border-gray-300 dark:border-gray-600", disabled && "cursor-not-allowed opacity-50"),
|
|
200
|
+
children: [showToolbar && jsx(RichTextToolbar, {
|
|
201
|
+
editor: editor,
|
|
202
|
+
state: state,
|
|
203
|
+
items: items,
|
|
204
|
+
disabled: disabled,
|
|
205
|
+
controlsId: editorId,
|
|
206
|
+
label: toolbarLabel
|
|
207
|
+
}), jsxs("div", {
|
|
208
|
+
className: cn("relative overflow-y-auto focus-within:ring-2 focus-within:ring-inset", error ? "focus-within:ring-error-500" : "focus-within:ring-primary-500"),
|
|
209
|
+
style: {
|
|
210
|
+
minHeight: toCss(minHeight),
|
|
211
|
+
maxHeight: toCss(maxHeight)
|
|
212
|
+
},
|
|
213
|
+
onMouseDown: function onMouseDown(e) {
|
|
214
|
+
if (e.target === e.currentTarget) {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
focusEditor();
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
children: [showPlaceholder && jsx("div", {
|
|
220
|
+
className: "pointer-events-none absolute left-3 top-3 select-none text-sm text-gray-400 dark:text-gray-500",
|
|
221
|
+
"aria-hidden": "true",
|
|
222
|
+
children: placeholder
|
|
223
|
+
}), jsx(EditorContent, {
|
|
224
|
+
editor: editor
|
|
225
|
+
})]
|
|
226
|
+
})]
|
|
227
|
+
}), error && jsxs("p", {
|
|
228
|
+
id: errorId,
|
|
229
|
+
className: "mt-1 flex items-center gap-1 text-sm text-error-600 dark:text-error-400",
|
|
230
|
+
"aria-live": "polite",
|
|
231
|
+
children: [jsx(AlertCircle, {
|
|
232
|
+
className: "h-4 w-4 shrink-0",
|
|
233
|
+
"aria-hidden": "true"
|
|
234
|
+
}), error]
|
|
235
|
+
}), helperText && jsx("p", {
|
|
236
|
+
id: helperId,
|
|
237
|
+
className: "mt-1 text-sm text-gray-500 dark:text-gray-400",
|
|
238
|
+
children: helperText
|
|
239
|
+
})]
|
|
240
|
+
}));
|
|
241
|
+
});
|
|
242
|
+
RichTextEditor.displayName = "RichTextEditor";
|
|
243
|
+
|
|
244
|
+
export { RichTextEditor as default };
|