@crewlethq/ui 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -5
- package/dist/AppShell/index.js +2 -1
- package/dist/Avatar/index.js +1 -1
- package/dist/{Avatar-QHYOFID3.css → Avatar-BPJ36QZR.css} +1 -1
- package/dist/AvatarPicker/index.js +5 -5
- package/dist/Breadcrumb/Breadcrumb.d.ts +68 -0
- package/dist/Breadcrumb/index.d.ts +3 -0
- package/dist/Breadcrumb/index.js +3 -0
- package/dist/Breadcrumb-YMWVM3SV.css +193 -0
- package/dist/ChoiceButtons/ChoiceButtons.d.ts +43 -0
- package/dist/ChoiceButtons/index.d.ts +3 -0
- package/dist/ChoiceButtons/index.js +3 -0
- package/dist/ChoiceButtons-DJ5EOGUA.css +127 -0
- package/dist/Combobox/index.js +2 -2
- package/dist/CommandPalette/CommandPalette.d.ts +48 -1
- package/dist/CommandPalette/index.js +3 -3
- package/dist/{CommandPalette-C5AMKGN2.css → CommandPalette-HBTNKHZQ.css} +55 -2
- package/dist/DataTable/index.js +4 -4
- package/dist/DataView/index.js +11 -11
- package/dist/DateTimePicker/index.js +2 -2
- package/dist/{DateTimePicker-4AZBTODT.css → DateTimePicker-NJ6XH2TE.css} +4 -3
- package/dist/Document/Document.d.ts +56 -0
- package/dist/Document/index.d.ts +3 -0
- package/dist/Document/index.js +2 -0
- package/dist/Document-PQGXJHB7.css +288 -0
- package/dist/EntityChip/index.js +1 -1
- package/dist/Highlight/Highlight.d.ts +45 -0
- package/dist/Highlight/index.d.ts +3 -0
- package/dist/Highlight/index.js +2 -0
- package/dist/Highlight-DXLV2R6L.css +29 -0
- package/dist/HuePicker/index.js +3 -3
- package/dist/ImageUpload/index.js +2 -2
- package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
- package/dist/InboxList/InboxList.d.ts +116 -0
- package/dist/InboxList/index.d.ts +3 -0
- package/dist/InboxList/index.js +5 -0
- package/dist/InboxList-YMFWKGGS.css +252 -0
- package/dist/KeyChip/KeyChip.d.ts +21 -0
- package/dist/KeyChip/index.d.ts +3 -0
- package/dist/KeyChip/index.js +2 -0
- package/dist/KeyChip-4XJU4BBE.css +30 -0
- package/dist/Link/Link.d.ts +17 -2
- package/dist/Link/index.js +1 -1
- package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
- package/dist/Listbox/index.d.ts +2 -0
- package/dist/Listbox/index.js +1 -1
- package/dist/Listbox/useHighlightRest.d.ts +35 -0
- package/dist/MarkdownEditor/MarkdownEditor.d.ts +93 -0
- package/dist/MarkdownEditor/format.d.ts +34 -0
- package/dist/MarkdownEditor/index.d.ts +5 -0
- package/dist/MarkdownEditor/index.js +12 -0
- package/dist/MarkdownEditor-ZLSHTOIZ.css +146 -0
- package/dist/Modal/Modal.d.ts +5 -1
- package/dist/Modal/index.js +1 -1
- package/dist/{Modal-BNT3TDV2.css → Modal-RQAYGG4L.css} +34 -1
- package/dist/OrgLabel/index.js +1 -1
- package/dist/OrgNode/index.js +1 -1
- package/dist/OrgTable/index.js +4 -4
- package/dist/Outline/Outline.d.ts +72 -0
- package/dist/Outline/index.d.ts +3 -0
- package/dist/Outline/index.js +2 -0
- package/dist/Outline-VCXKKU3E.css +78 -0
- package/dist/Popover/Popover.d.ts +21 -1
- package/dist/Popover/index.js +1 -1
- package/dist/ReadingPane/ReadingPane.d.ts +95 -0
- package/dist/ReadingPane/index.d.ts +3 -0
- package/dist/ReadingPane/index.js +4 -0
- package/dist/ReadingPane-5UOLPWY5.css +194 -0
- package/dist/SegmentedControl/SegmentedControl.d.ts +9 -1
- package/dist/SegmentedControl/index.js +3 -3
- package/dist/{SegmentedControl-76HODRBT.css → SegmentedControl-PKBLJVSA.css} +18 -0
- package/dist/Select/Select.d.ts +22 -0
- package/dist/Select/index.js +2 -2
- package/dist/{Select-4ANVLHUY.css → Select-2P7AWUE4.css} +56 -6
- package/dist/SidebarNav/index.js +2 -1
- package/dist/{SidebarNav-X5NLZNWA.css → SidebarNav-KF66MLOM.css} +4 -19
- package/dist/Switcher/Switcher.d.ts +151 -0
- package/dist/Switcher/index.d.ts +3 -0
- package/dist/Switcher/index.js +16 -0
- package/dist/Switcher-B44Q4WF4.css +346 -0
- package/dist/Tabs/Tabs.d.ts +7 -1
- package/dist/Tabs/index.js +1 -1
- package/dist/{Tabs-5I5Q5KKM.css → Tabs-ITJYJFMU.css} +49 -0
- package/dist/TagsInput/index.js +2 -2
- package/dist/ThemeGallery/ThemeGallery.d.ts +65 -0
- package/dist/ThemeGallery/index.d.ts +3 -0
- package/dist/ThemeGallery/index.js +6 -0
- package/dist/ThemeGallery-NITASULQ.css +93 -0
- package/dist/ThemeSwatch/ThemeSwatch.d.ts +71 -0
- package/dist/ThemeSwatch/index.d.ts +3 -0
- package/dist/ThemeSwatch/index.js +2 -0
- package/dist/ThemeSwatch-4DL457ZB.css +256 -0
- package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +5 -1
- package/dist/ThemeSwitcher/index.js +4 -4
- package/dist/ThemeToggle/ThemeToggle.d.ts +1 -1
- package/dist/ThemeToggle/index.js +1 -1
- package/dist/TimeWindowPicker/index.js +4 -4
- package/dist/{TimeWindowPicker-GBF4JDFI.css → TimeWindowPicker-BZ5HHATU.css} +5 -4
- package/dist/Toaster/index.js +1 -1
- package/dist/{Toaster-NVDCEIP5.css → Toaster-VZKISP2S.css} +81 -87
- package/dist/Tree/index.d.ts +1 -1
- package/dist/Tree/index.js +1 -1
- package/dist/Tree/useTreeState.d.ts +37 -7
- package/dist/TreeCanvas/index.js +2 -2
- package/dist/TreeGrid/TreeGrid.d.ts +22 -2
- package/dist/TreeGrid/index.js +2 -2
- package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
- package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
- package/dist/{chunk-GOUKOXJK.js → chunk-3VVKJ7AZ.js} +3 -3
- package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
- package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
- package/dist/{chunk-GXYNEDNA.js → chunk-4Y7WKXAW.js} +1 -1
- package/dist/{chunk-IINFVK3W.js → chunk-5IXMLWGW.js} +6 -6
- package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
- package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
- package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
- package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
- package/dist/chunk-77IEHFY4.js +299 -0
- package/dist/chunk-A5YKNRG6.js +94 -0
- package/dist/{chunk-JCNUQ3US.js → chunk-AXNQCAFC.js} +2 -2
- package/dist/{chunk-UG5ZMAXW.js → chunk-CBZSSKH3.js} +5 -5
- package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
- package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
- package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
- package/dist/chunk-FYTIJM2T.js +40 -0
- package/dist/chunk-H6VKCIWV.js +41 -0
- package/dist/{chunk-HLBRPES3.js → chunk-IFM63LAG.js} +3 -3
- package/dist/chunk-IG75YMUN.js +22 -0
- package/dist/{chunk-SEFMO2EV.js → chunk-IXUU5DLZ.js} +55 -6
- package/dist/{chunk-ZHOM65MI.js → chunk-JCO3SIST.js} +1 -1
- package/dist/chunk-KLIEPFEZ.js +95 -0
- package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
- package/dist/chunk-MNFU2RNT.js +74 -0
- package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
- package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
- package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
- package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
- package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
- package/dist/{chunk-TEEJSTFG.js → chunk-TFMJH2E6.js} +1 -1
- package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
- package/dist/chunk-VWNC2L6W.js +110 -0
- package/dist/chunk-VYY56AMX.js +2 -0
- package/dist/chunk-WI6K5M5X.js +24 -0
- package/dist/{chunk-XV65NTIT.js → chunk-WYQOHFML.js} +94 -41
- package/dist/chunk-XYCADWZN.js +100 -0
- package/dist/chunk-Y6NR7QJ3.js +336 -0
- package/dist/chunk-YSJC7AZC.js +62 -0
- package/dist/chunk-ZAJHGDAW.js +93 -0
- package/dist/generated/index.d.ts +12 -0
- package/dist/generated/index.js +64 -51
- package/dist/index.js +64 -51
- package/dist/styles.css +2711 -402
- package/dist/utils/highlight.d.ts +17 -0
- package/dist/utils/useMediaMatch.d.ts +12 -0
- package/package.json +3 -3
- package/dist/chunk-UAQIUSSK.js +0 -2
- /package/dist/{chunk-74OVQWA5.js → chunk-6AR2T5Y4.js} +0 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { Toolbar } from './chunk-PGRPNDWM.js';
|
|
2
|
+
import { IconButton } from './chunk-4FQT6ZAY.js';
|
|
3
|
+
import { SegmentedControl } from './chunk-26PEPONN.js';
|
|
4
|
+
import { tabId } from './chunk-4LDTQWS7.js';
|
|
5
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
6
|
+
import './MarkdownEditor-ZLSHTOIZ.css';
|
|
7
|
+
import { useState, useId, useRef, useLayoutEffect, useImperativeHandle } from 'react';
|
|
8
|
+
import { TableGlyph, ListChecksGlyph, ListOrderedGlyph, ListGlyph, CodeGlyph, ItalicGlyph, BoldGlyph, Heading3Glyph, Heading2Glyph } from '@crewlethq/icons/glyphs';
|
|
9
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
// src/MarkdownEditor/format.ts
|
|
12
|
+
var MARKDOWN_FORMATS = [
|
|
13
|
+
"heading2",
|
|
14
|
+
"heading3",
|
|
15
|
+
"bold",
|
|
16
|
+
"italic",
|
|
17
|
+
"code",
|
|
18
|
+
"bulletList",
|
|
19
|
+
"numberedList",
|
|
20
|
+
"checklist",
|
|
21
|
+
"table"
|
|
22
|
+
];
|
|
23
|
+
var WRAP = { bold: "**", italic: "_", code: "`" };
|
|
24
|
+
var LINE_MARKER = /^(#{1,6} |- \[[ xX]\] |[-*+] |\d+[.)] )/;
|
|
25
|
+
function insertMarkdown(value, { start, end }, text) {
|
|
26
|
+
const next = value.slice(0, start) + text + value.slice(end);
|
|
27
|
+
const caret = start + text.length;
|
|
28
|
+
return { value: next, selection: { start: caret, end: caret } };
|
|
29
|
+
}
|
|
30
|
+
function formatMarkdown(value, selection, format) {
|
|
31
|
+
switch (format) {
|
|
32
|
+
case "bold":
|
|
33
|
+
case "italic":
|
|
34
|
+
case "code":
|
|
35
|
+
return wrap(value, selection, WRAP[format]);
|
|
36
|
+
case "heading2":
|
|
37
|
+
return prefixLines(value, selection, () => "## ", (marker) => marker === "## ");
|
|
38
|
+
case "heading3":
|
|
39
|
+
return prefixLines(value, selection, () => "### ", (marker) => marker === "### ");
|
|
40
|
+
case "bulletList":
|
|
41
|
+
return prefixLines(value, selection, () => "- ", (marker) => /^[-*+] $/.test(marker));
|
|
42
|
+
case "numberedList":
|
|
43
|
+
return prefixLines(value, selection, (index) => `${index + 1}. `, (marker) => /^\d+[.)] $/.test(marker));
|
|
44
|
+
case "checklist":
|
|
45
|
+
return prefixLines(value, selection, () => "- [ ] ", (marker) => /^- \[[ xX]\] $/.test(marker));
|
|
46
|
+
case "table":
|
|
47
|
+
return table(value, selection);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function wrap(value, selection, marker) {
|
|
51
|
+
let { start, end } = selection;
|
|
52
|
+
while (start < end && /\s/.test(value[start] ?? "")) start += 1;
|
|
53
|
+
while (end > start && /\s/.test(value[end - 1] ?? "")) end -= 1;
|
|
54
|
+
const size = marker.length;
|
|
55
|
+
if (value.slice(start - size, start) === marker && value.slice(end, end + size) === marker) {
|
|
56
|
+
return {
|
|
57
|
+
value: value.slice(0, start - size) + value.slice(start, end) + value.slice(end + size),
|
|
58
|
+
selection: { start: start - size, end: end - size }
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const selected = value.slice(start, end);
|
|
62
|
+
if (selected.length >= size * 2 && selected.startsWith(marker) && selected.endsWith(marker)) {
|
|
63
|
+
const inner = selected.slice(size, selected.length - size);
|
|
64
|
+
return { value: value.slice(0, start) + inner + value.slice(end), selection: { start, end: start + inner.length } };
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
value: value.slice(0, start) + marker + selected + marker + value.slice(end),
|
|
68
|
+
selection: { start: start + size, end: end + size }
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function lineSpan(value, { start, end }) {
|
|
72
|
+
const from = value.lastIndexOf("\n", start - 1) + 1;
|
|
73
|
+
const last = end > start && value[end - 1] === "\n" ? end - 1 : end;
|
|
74
|
+
const newline = value.indexOf("\n", last);
|
|
75
|
+
return [from, newline === -1 ? value.length : newline];
|
|
76
|
+
}
|
|
77
|
+
function prefixLines(value, selection, marker, has) {
|
|
78
|
+
const [from, to] = lineSpan(value, selection);
|
|
79
|
+
const lines = value.slice(from, to).split("\n");
|
|
80
|
+
const single = lines.length === 1;
|
|
81
|
+
const written = lines.filter((line) => single || line.trim() !== "");
|
|
82
|
+
const off = written.length > 0 && written.every((line) => has(LINE_MARKER.exec(line)?.[0] ?? ""));
|
|
83
|
+
let index = 0;
|
|
84
|
+
let caretShift = 0;
|
|
85
|
+
const caretLine = value.slice(from, selection.start).split("\n").length - 1;
|
|
86
|
+
const next = lines.map((line, at) => {
|
|
87
|
+
if (!single && line.trim() === "") return line;
|
|
88
|
+
const old = LINE_MARKER.exec(line)?.[0] ?? "";
|
|
89
|
+
const body = line.slice(old.length);
|
|
90
|
+
const added = off ? "" : marker(index);
|
|
91
|
+
index += 1;
|
|
92
|
+
if (at === caretLine) caretShift = added.length - old.length;
|
|
93
|
+
return added + body;
|
|
94
|
+
});
|
|
95
|
+
const block = next.join("\n");
|
|
96
|
+
const result = value.slice(0, from) + block + value.slice(to);
|
|
97
|
+
if (selection.start === selection.end) {
|
|
98
|
+
const caret = Math.max(from, selection.start + caretShift);
|
|
99
|
+
return { value: result, selection: { start: caret, end: caret } };
|
|
100
|
+
}
|
|
101
|
+
return { value: result, selection: { start: from, end: from + block.length } };
|
|
102
|
+
}
|
|
103
|
+
var TABLE = "| Column | Column |\n| --- | --- |\n| | |";
|
|
104
|
+
function table(value, { start, end }) {
|
|
105
|
+
const before = value.slice(0, start);
|
|
106
|
+
const after = value.slice(end);
|
|
107
|
+
const lead = before === "" ? "" : before.endsWith("\n\n") ? "" : before.endsWith("\n") ? "\n" : "\n\n";
|
|
108
|
+
const trail = after === "" ? "" : after.startsWith("\n\n") ? "" : after.startsWith("\n") ? "\n" : "\n\n";
|
|
109
|
+
const at = before.length + lead.length + 2;
|
|
110
|
+
return {
|
|
111
|
+
value: before + lead + TABLE + trail + after,
|
|
112
|
+
selection: { start: at, end: at + "Column".length }
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function markdownWords(value) {
|
|
116
|
+
return (value.match(/[\p{L}\p{N}][\p{L}\p{N}'’-]*/gu) ?? []).length;
|
|
117
|
+
}
|
|
118
|
+
var LABELS = {
|
|
119
|
+
toolbar: "Formatting",
|
|
120
|
+
view: "Editor view",
|
|
121
|
+
write: "Write",
|
|
122
|
+
preview: "Preview",
|
|
123
|
+
empty: "Nothing to preview yet.",
|
|
124
|
+
formats: {
|
|
125
|
+
heading2: "Heading",
|
|
126
|
+
heading3: "Subheading",
|
|
127
|
+
bold: "Bold",
|
|
128
|
+
italic: "Italic",
|
|
129
|
+
code: "Code",
|
|
130
|
+
bulletList: "Bulleted list",
|
|
131
|
+
numberedList: "Numbered list",
|
|
132
|
+
checklist: "Checklist",
|
|
133
|
+
table: "Table"
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var GROUPS = [
|
|
137
|
+
["heading2", "heading3"],
|
|
138
|
+
["bold", "italic", "code"],
|
|
139
|
+
["bulletList", "numberedList", "checklist", "table"]
|
|
140
|
+
];
|
|
141
|
+
var GLYPHS = {
|
|
142
|
+
heading2: /* @__PURE__ */ jsx(Heading2Glyph, {}),
|
|
143
|
+
heading3: /* @__PURE__ */ jsx(Heading3Glyph, {}),
|
|
144
|
+
bold: /* @__PURE__ */ jsx(BoldGlyph, {}),
|
|
145
|
+
italic: /* @__PURE__ */ jsx(ItalicGlyph, {}),
|
|
146
|
+
code: /* @__PURE__ */ jsx(CodeGlyph, {}),
|
|
147
|
+
bulletList: /* @__PURE__ */ jsx(ListGlyph, {}),
|
|
148
|
+
numberedList: /* @__PURE__ */ jsx(ListOrderedGlyph, {}),
|
|
149
|
+
checklist: /* @__PURE__ */ jsx(ListChecksGlyph, {}),
|
|
150
|
+
table: /* @__PURE__ */ jsx(TableGlyph, {})
|
|
151
|
+
};
|
|
152
|
+
function MarkdownEditor({
|
|
153
|
+
value,
|
|
154
|
+
onChange,
|
|
155
|
+
label,
|
|
156
|
+
renderPreview,
|
|
157
|
+
placeholder,
|
|
158
|
+
mode: controlled,
|
|
159
|
+
defaultMode = "write",
|
|
160
|
+
onModeChange,
|
|
161
|
+
actions,
|
|
162
|
+
footer,
|
|
163
|
+
onSubmit,
|
|
164
|
+
disabled = false,
|
|
165
|
+
invalid = false,
|
|
166
|
+
rows = 14,
|
|
167
|
+
id,
|
|
168
|
+
"aria-describedby": describedBy,
|
|
169
|
+
labels,
|
|
170
|
+
ref,
|
|
171
|
+
className
|
|
172
|
+
}) {
|
|
173
|
+
const text = { ...LABELS, ...labels, formats: { ...LABELS.formats, ...labels?.formats } };
|
|
174
|
+
const [own, setOwn] = useState(defaultMode);
|
|
175
|
+
const mode = controlled ?? own;
|
|
176
|
+
const setMode = (next) => {
|
|
177
|
+
if (controlled === void 0) setOwn(next);
|
|
178
|
+
onModeChange?.(next);
|
|
179
|
+
};
|
|
180
|
+
const panelId = useId();
|
|
181
|
+
const field = useRef(null);
|
|
182
|
+
const pending = useRef(null);
|
|
183
|
+
useLayoutEffect(() => {
|
|
184
|
+
const el = field.current;
|
|
185
|
+
const selection2 = pending.current;
|
|
186
|
+
if (!el || !selection2) return;
|
|
187
|
+
pending.current = null;
|
|
188
|
+
el.focus();
|
|
189
|
+
el.setSelectionRange(selection2.start, selection2.end);
|
|
190
|
+
}, [value]);
|
|
191
|
+
const selection = () => {
|
|
192
|
+
const el = field.current;
|
|
193
|
+
return el ? { start: el.selectionStart, end: el.selectionEnd } : { start: value.length, end: value.length };
|
|
194
|
+
};
|
|
195
|
+
const apply = (edit) => {
|
|
196
|
+
if (edit.value === value) return;
|
|
197
|
+
pending.current = edit.selection;
|
|
198
|
+
onChange(edit.value);
|
|
199
|
+
};
|
|
200
|
+
const editor = {
|
|
201
|
+
mode,
|
|
202
|
+
insert: (inserted) => apply(insertMarkdown(value, selection(), inserted)),
|
|
203
|
+
format: (format) => apply(formatMarkdown(value, selection(), format)),
|
|
204
|
+
focus: () => field.current?.focus()
|
|
205
|
+
};
|
|
206
|
+
useImperativeHandle(ref, () => editor);
|
|
207
|
+
const onKeyDown = (event) => {
|
|
208
|
+
const command = event.metaKey || event.ctrlKey;
|
|
209
|
+
if (!command || event.altKey) return;
|
|
210
|
+
if (event.key === "Enter" && onSubmit) {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
onSubmit();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (event.shiftKey) return;
|
|
216
|
+
const key = event.key.toLowerCase();
|
|
217
|
+
if (key === "b" || key === "i") {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
editor.format(key === "b" ? "bold" : "italic");
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const writing = mode === "write";
|
|
223
|
+
return /* @__PURE__ */ jsxs(
|
|
224
|
+
"div",
|
|
225
|
+
{
|
|
226
|
+
className: cx(
|
|
227
|
+
"crewlet-markdown-editor",
|
|
228
|
+
invalid && "crewlet-markdown-editor--invalid",
|
|
229
|
+
disabled && "crewlet-markdown-editor--disabled",
|
|
230
|
+
className
|
|
231
|
+
),
|
|
232
|
+
style: { "--crewlet-markdown-editor-rows": rows },
|
|
233
|
+
children: [
|
|
234
|
+
/* @__PURE__ */ jsxs(Toolbar, { label: text.toolbar, className: "crewlet-markdown-editor__bar", children: [
|
|
235
|
+
GROUPS.map((group, index) => /* @__PURE__ */ jsx("span", { className: "crewlet-markdown-editor__group", children: group.map((format) => /* @__PURE__ */ jsx(
|
|
236
|
+
IconButton,
|
|
237
|
+
{
|
|
238
|
+
size: "sm",
|
|
239
|
+
variant: "ghost",
|
|
240
|
+
label: text.formats[format],
|
|
241
|
+
icon: GLYPHS[format],
|
|
242
|
+
disabled: disabled || !writing,
|
|
243
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
244
|
+
onClick: () => editor.format(format)
|
|
245
|
+
},
|
|
246
|
+
format
|
|
247
|
+
)) }, index)),
|
|
248
|
+
actions && /* @__PURE__ */ jsx("span", { className: "crewlet-markdown-editor__group", children: actions(editor) }),
|
|
249
|
+
/* @__PURE__ */ jsx("span", { className: "crewlet-markdown-editor__spacer" }),
|
|
250
|
+
/* @__PURE__ */ jsx(
|
|
251
|
+
SegmentedControl,
|
|
252
|
+
{
|
|
253
|
+
semantics: "tabs",
|
|
254
|
+
panelId,
|
|
255
|
+
size: "sm",
|
|
256
|
+
label: text.view,
|
|
257
|
+
options: [
|
|
258
|
+
{ value: "write", label: text.write },
|
|
259
|
+
{ value: "preview", label: text.preview }
|
|
260
|
+
],
|
|
261
|
+
value: mode,
|
|
262
|
+
onValueChange: setMode
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
] }),
|
|
266
|
+
/* @__PURE__ */ jsx(
|
|
267
|
+
"div",
|
|
268
|
+
{
|
|
269
|
+
role: "tabpanel",
|
|
270
|
+
id: panelId,
|
|
271
|
+
"aria-labelledby": tabId(panelId, mode),
|
|
272
|
+
className: "crewlet-markdown-editor__panel",
|
|
273
|
+
children: writing ? /* @__PURE__ */ jsx(
|
|
274
|
+
"textarea",
|
|
275
|
+
{
|
|
276
|
+
ref: field,
|
|
277
|
+
id,
|
|
278
|
+
className: "crewlet-markdown-editor__field",
|
|
279
|
+
value,
|
|
280
|
+
onChange: (event) => onChange(event.target.value),
|
|
281
|
+
onKeyDown,
|
|
282
|
+
"aria-label": label,
|
|
283
|
+
"aria-describedby": describedBy,
|
|
284
|
+
"aria-invalid": invalid || void 0,
|
|
285
|
+
placeholder,
|
|
286
|
+
disabled,
|
|
287
|
+
rows,
|
|
288
|
+
spellCheck: true
|
|
289
|
+
}
|
|
290
|
+
) : /* @__PURE__ */ jsx("div", { className: "crewlet-markdown-editor__preview", children: value.trim() === "" ? /* @__PURE__ */ jsx("p", { className: "crewlet-markdown-editor__empty", children: text.empty }) : renderPreview(value) })
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
footer !== void 0 && footer !== null && /* @__PURE__ */ jsx("div", { className: "crewlet-markdown-editor__footer", children: footer })
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export { MARKDOWN_FORMATS, MarkdownEditor, formatMarkdown, insertMarkdown, markdownWords };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
2
|
+
import './Outline-VCXKKU3E.css';
|
|
3
|
+
import { useState, useRef, useEffect } from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function Outline({ label, items, activeId = null, onSelect, className, ...rest }) {
|
|
7
|
+
const shallowest = items.reduce((min, item) => Math.min(min, item.level), Number.POSITIVE_INFINITY);
|
|
8
|
+
const select = onSelect ?? ((id) => scrollToHeading(id));
|
|
9
|
+
return /* @__PURE__ */ jsxs("nav", { ...rest, "aria-label": label, className: cx("crewlet-outline", className), children: [
|
|
10
|
+
/* @__PURE__ */ jsx("p", { className: "crewlet-outline__title", "aria-hidden": "true", children: label }),
|
|
11
|
+
/* @__PURE__ */ jsx("ol", { className: "crewlet-outline__list", children: items.map((item) => {
|
|
12
|
+
const current = item.id === activeId;
|
|
13
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
14
|
+
"button",
|
|
15
|
+
{
|
|
16
|
+
type: "button",
|
|
17
|
+
className: cx("crewlet-outline__item", current && "crewlet-outline__item--current"),
|
|
18
|
+
style: { "--crewlet-outline-depth": item.level - shallowest },
|
|
19
|
+
"aria-current": current ? "location" : void 0,
|
|
20
|
+
onClick: () => select(item.id),
|
|
21
|
+
children: item.label
|
|
22
|
+
}
|
|
23
|
+
) }, item.id);
|
|
24
|
+
}) })
|
|
25
|
+
] });
|
|
26
|
+
}
|
|
27
|
+
function scrollToHeading(id, ownerDocument = document) {
|
|
28
|
+
const heading = ownerDocument.getElementById(id);
|
|
29
|
+
if (!heading) return false;
|
|
30
|
+
if (!heading.hasAttribute("tabindex")) heading.setAttribute("tabindex", "-1");
|
|
31
|
+
const still = ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
|
|
32
|
+
heading.scrollIntoView?.({ behavior: still ? "auto" : "smooth", block: "start" });
|
|
33
|
+
heading.focus({ preventScroll: true });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function nextFrame(view, run) {
|
|
37
|
+
if (typeof view.requestAnimationFrame === "function") {
|
|
38
|
+
const frame = view.requestAnimationFrame(run);
|
|
39
|
+
return () => view.cancelAnimationFrame(frame);
|
|
40
|
+
}
|
|
41
|
+
const timer = view.setTimeout(run, 0);
|
|
42
|
+
return () => view.clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
function useActiveHeading(ids, { root = null, offset = 0 } = {}) {
|
|
45
|
+
const key = ids.join("\0");
|
|
46
|
+
const [active, setActive] = useState(ids[0] ?? null);
|
|
47
|
+
const latest = useRef(active);
|
|
48
|
+
latest.current = active;
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const list = key === "" ? [] : key.split("\0");
|
|
51
|
+
const owner = root?.ownerDocument ?? document;
|
|
52
|
+
const view = owner.defaultView;
|
|
53
|
+
if (!view) return void 0;
|
|
54
|
+
const measure = () => {
|
|
55
|
+
if (list.length === 0) {
|
|
56
|
+
if (latest.current !== null) setActive(null);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const top = root ? root.getBoundingClientRect().top : 0;
|
|
60
|
+
let current = list[0] ?? null;
|
|
61
|
+
for (const id of list) {
|
|
62
|
+
const heading = owner.getElementById(id);
|
|
63
|
+
if (!heading) continue;
|
|
64
|
+
if (heading.getBoundingClientRect().top - top <= offset + 1) current = id;
|
|
65
|
+
else break;
|
|
66
|
+
}
|
|
67
|
+
const scrolled = root ? root.scrollTop : view.scrollY;
|
|
68
|
+
const height = root ? root.clientHeight : view.innerHeight;
|
|
69
|
+
const extent = root ? root.scrollHeight : owner.documentElement.scrollHeight;
|
|
70
|
+
if (scrolled > 0 && scrolled + height >= extent - 1) current = list[list.length - 1] ?? current;
|
|
71
|
+
if (current !== latest.current) setActive(current);
|
|
72
|
+
};
|
|
73
|
+
let cancel = null;
|
|
74
|
+
const schedule = () => {
|
|
75
|
+
if (cancel) return;
|
|
76
|
+
cancel = nextFrame(view, () => {
|
|
77
|
+
cancel = null;
|
|
78
|
+
measure();
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
schedule();
|
|
82
|
+
const target = root ?? view;
|
|
83
|
+
target.addEventListener("scroll", schedule, { passive: true });
|
|
84
|
+
view.addEventListener("resize", schedule);
|
|
85
|
+
return () => {
|
|
86
|
+
target.removeEventListener("scroll", schedule);
|
|
87
|
+
view.removeEventListener("resize", schedule);
|
|
88
|
+
cancel?.();
|
|
89
|
+
};
|
|
90
|
+
}, [key, root, offset]);
|
|
91
|
+
return active;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { Outline, scrollToHeading, useActiveHeading };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { today, CalendarGrid, shiftMonth, compareDays } from './chunk-GXQ3B46I.js';
|
|
2
2
|
import { Button } from './chunk-OG6CL3EB.js';
|
|
3
3
|
import { IconButton } from './chunk-4FQT6ZAY.js';
|
|
4
|
-
import { Popover } from './chunk-
|
|
4
|
+
import { Popover } from './chunk-O2ZDZZS7.js';
|
|
5
5
|
import { cx } from './chunk-ODEQTPFM.js';
|
|
6
|
-
import './DateTimePicker-
|
|
6
|
+
import './DateTimePicker-NJ6XH2TE.css';
|
|
7
7
|
import { useMemo, useState, useEffect } from 'react';
|
|
8
8
|
import { CalendarGlyph, ChevronDownGlyph, ChevronsLeftGlyph, ChevronLeftGlyph, ChevronRightGlyph, ChevronsRightGlyph } from '@crewlethq/icons/glyphs';
|
|
9
9
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { useLayerContainer } from './chunk-7RPGY3JE.js';
|
|
2
2
|
import { VisuallyHidden } from './chunk-BZFWDIV6.js';
|
|
3
3
|
import { cx } from './chunk-ODEQTPFM.js';
|
|
4
|
-
import './Toaster-
|
|
4
|
+
import './Toaster-VZKISP2S.css';
|
|
5
5
|
import { createContext, useMemo, useState, useRef, useEffect, useContext, useCallback } from 'react';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
7
7
|
import { XGlyph, CircleAlertGlyph, TriangleAlertGlyph, CircleCheckGlyph, InfoGlyph } from '@crewlethq/icons/glyphs';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
9
|
|
|
10
10
|
var ICONS = {
|
|
11
|
-
info: /* @__PURE__ */ jsx(InfoGlyph, { size: "
|
|
12
|
-
success: /* @__PURE__ */ jsx(CircleCheckGlyph, { size: "
|
|
13
|
-
warning: /* @__PURE__ */ jsx(TriangleAlertGlyph, { size: "
|
|
14
|
-
danger: /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "
|
|
11
|
+
info: /* @__PURE__ */ jsx(InfoGlyph, { size: "lg" }),
|
|
12
|
+
success: /* @__PURE__ */ jsx(CircleCheckGlyph, { size: "lg" }),
|
|
13
|
+
warning: /* @__PURE__ */ jsx(TriangleAlertGlyph, { size: "lg" }),
|
|
14
|
+
danger: /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "lg" })
|
|
15
15
|
};
|
|
16
16
|
var TITLES = {
|
|
17
17
|
info: null,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { avatarCorner, Avatar } from './chunk-HI6Q2UXQ.js';
|
|
2
|
-
import './ImageUpload-
|
|
2
|
+
import './ImageUpload-NVGMN7DP.css';
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { CameraGlyph, XGlyph } from '@crewlethq/icons/glyphs';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { usePopupLayer, isComposing } from './chunk-R25RGPUB.js';
|
|
2
2
|
import './Listbox-6WJNJQVQ.css';
|
|
3
|
-
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
3
|
+
import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
4
4
|
|
|
5
5
|
function useListbox({
|
|
6
6
|
id,
|
|
@@ -146,5 +146,50 @@ function useOptionKeys({ listbox, count, disabled, textOf, ends = true }) {
|
|
|
146
146
|
);
|
|
147
147
|
return { onKeyDown, step, opening };
|
|
148
148
|
}
|
|
149
|
+
var HIGHLIGHT_REST_MS = 120;
|
|
150
|
+
function useHighlightRest(onRest) {
|
|
151
|
+
const told = useRef(null);
|
|
152
|
+
const timer = useRef(null);
|
|
153
|
+
const callback = useRef(onRest);
|
|
154
|
+
callback.current = onRest;
|
|
155
|
+
const cancel = useCallback(() => {
|
|
156
|
+
if (timer.current === null) return;
|
|
157
|
+
clearTimeout(timer.current);
|
|
158
|
+
timer.current = null;
|
|
159
|
+
}, []);
|
|
160
|
+
const tell = useCallback((key) => {
|
|
161
|
+
if (told.current === key) return;
|
|
162
|
+
told.current = key;
|
|
163
|
+
callback.current?.(key);
|
|
164
|
+
}, []);
|
|
165
|
+
const now = useCallback(
|
|
166
|
+
(key) => {
|
|
167
|
+
cancel();
|
|
168
|
+
tell(key);
|
|
169
|
+
},
|
|
170
|
+
[cancel, tell]
|
|
171
|
+
);
|
|
172
|
+
const rest = useCallback(
|
|
173
|
+
(key) => {
|
|
174
|
+
cancel();
|
|
175
|
+
timer.current = setTimeout(() => {
|
|
176
|
+
timer.current = null;
|
|
177
|
+
tell(key);
|
|
178
|
+
}, HIGHLIGHT_REST_MS);
|
|
179
|
+
},
|
|
180
|
+
[cancel, tell]
|
|
181
|
+
);
|
|
182
|
+
useEffect(
|
|
183
|
+
() => () => {
|
|
184
|
+
cancel();
|
|
185
|
+
if (told.current !== null) {
|
|
186
|
+
told.current = null;
|
|
187
|
+
callback.current?.(null);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
[cancel]
|
|
191
|
+
);
|
|
192
|
+
return useMemo(() => ({ now, rest }), [now, rest]);
|
|
193
|
+
}
|
|
149
194
|
|
|
150
|
-
export { TYPE_AHEAD_WINDOW, useListbox, useOptionKeys };
|
|
195
|
+
export { HIGHLIGHT_REST_MS, TYPE_AHEAD_WINDOW, useHighlightRest, useListbox, useOptionKeys };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { VisuallyHidden } from './chunk-BZFWDIV6.js';
|
|
2
2
|
import { cx } from './chunk-ODEQTPFM.js';
|
|
3
|
-
import './Link-
|
|
3
|
+
import './Link-WPMVLKLY.css';
|
|
4
4
|
import { forwardRef, Children, isValidElement, cloneElement } from 'react';
|
|
5
5
|
import { ExternalLinkGlyph } from '@crewlethq/icons/glyphs';
|
|
6
6
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
2
|
+
import './KeyChip-4XJU4BBE.css';
|
|
3
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var WORD_BREAK = /[\s\-_/·]/;
|
|
6
|
+
function findMatch(text, query, prefer) {
|
|
7
|
+
const wanted = query.trim().toLowerCase();
|
|
8
|
+
if (wanted === "") return null;
|
|
9
|
+
const low = text.toLowerCase();
|
|
10
|
+
if (low.length !== text.length) return null;
|
|
11
|
+
let at = -1;
|
|
12
|
+
if (prefer === "start") {
|
|
13
|
+
at = low.startsWith(wanted) ? 0 : -1;
|
|
14
|
+
} else {
|
|
15
|
+
for (let i = 0; i < low.length; i += 1) {
|
|
16
|
+
const atWord = i === 0 || WORD_BREAK.test(low.charAt(i - 1));
|
|
17
|
+
if (atWord && low.startsWith(wanted, i)) {
|
|
18
|
+
at = i;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (at < 0) at = low.indexOf(wanted);
|
|
24
|
+
return at < 0 ? null : [at, at + wanted.length];
|
|
25
|
+
}
|
|
26
|
+
function highlightMatch(text, query, prefer, className) {
|
|
27
|
+
const range = query ? findMatch(text, query, prefer) : null;
|
|
28
|
+
if (!range) return text;
|
|
29
|
+
const [from, to] = range;
|
|
30
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
31
|
+
text.slice(0, from),
|
|
32
|
+
/* @__PURE__ */ jsx("b", { className, children: text.slice(from, to) }),
|
|
33
|
+
text.slice(to)
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
function KeyChip({ children, highlight, className, ...rest }) {
|
|
37
|
+
return /* @__PURE__ */ jsx("span", { ...rest, className: cx("crewlet-key-chip", className), children: highlightMatch(children, highlight, "start", "crewlet-key-chip__match") });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { KeyChip, highlightMatch };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cx } from './chunk-ODEQTPFM.js';
|
|
2
|
+
import './Highlight-DXLV2R6L.css';
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function highlightTerms(query) {
|
|
7
|
+
const words = query.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((word) => [...word].length >= 2);
|
|
8
|
+
return [...new Set(words)].sort((a, b) => b.length - a.length || a.localeCompare(b));
|
|
9
|
+
}
|
|
10
|
+
var escape = (term) => term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11
|
+
function highlightRanges(text, terms) {
|
|
12
|
+
const wanted = terms.map((term) => term.trim()).filter((term) => term !== "");
|
|
13
|
+
if (text === "" || wanted.length === 0) return [];
|
|
14
|
+
const ordered = [...new Set(wanted)].sort((a, b) => b.length - a.length);
|
|
15
|
+
const pattern = new RegExp(`(?<![\\p{L}\\p{N}])(?:${ordered.map(escape).join("|")})`, "giu");
|
|
16
|
+
const ranges = [];
|
|
17
|
+
for (const match of text.matchAll(pattern)) {
|
|
18
|
+
const start = match.index ?? 0;
|
|
19
|
+
const end = start + match[0].length;
|
|
20
|
+
const last = ranges[ranges.length - 1];
|
|
21
|
+
if (last && start <= last[1]) ranges[ranges.length - 1] = [last[0], Math.max(last[1], end)];
|
|
22
|
+
else ranges.push([start, end]);
|
|
23
|
+
}
|
|
24
|
+
return ranges;
|
|
25
|
+
}
|
|
26
|
+
function Highlight({ text, terms, className, ...rest }) {
|
|
27
|
+
const ranges = highlightRanges(text, terms);
|
|
28
|
+
const parts = [];
|
|
29
|
+
let at = 0;
|
|
30
|
+
ranges.forEach(([start, end], index) => {
|
|
31
|
+
if (start > at) parts.push(/* @__PURE__ */ jsx(Fragment, { children: text.slice(at, start) }, `t${index}`));
|
|
32
|
+
parts.push(
|
|
33
|
+
/* @__PURE__ */ jsx("mark", { className: "crewlet-highlight__match", children: text.slice(start, end) }, `m${index}`)
|
|
34
|
+
);
|
|
35
|
+
at = end;
|
|
36
|
+
});
|
|
37
|
+
if (at < text.length) parts.push(/* @__PURE__ */ jsx(Fragment, { children: text.slice(at) }, "tail"));
|
|
38
|
+
return /* @__PURE__ */ jsx("span", { ...rest, className: cx("crewlet-highlight", className), children: parts });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { Highlight, highlightRanges, highlightTerms };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Select } from './chunk-
|
|
1
|
+
import { Select } from './chunk-IXUU5DLZ.js';
|
|
2
2
|
import { Input } from './chunk-FTEJPHBI.js';
|
|
3
3
|
import { today, shiftMonth, CalendarGrid, compareDays } from './chunk-GXQ3B46I.js';
|
|
4
4
|
import { Button } from './chunk-OG6CL3EB.js';
|
|
5
5
|
import { IconButton } from './chunk-4FQT6ZAY.js';
|
|
6
|
-
import { Popover } from './chunk-
|
|
6
|
+
import { Popover } from './chunk-O2ZDZZS7.js';
|
|
7
7
|
import { isComposing } from './chunk-R25RGPUB.js';
|
|
8
8
|
import { cx } from './chunk-ODEQTPFM.js';
|
|
9
|
-
import './TimeWindowPicker-
|
|
9
|
+
import './TimeWindowPicker-BZ5HHATU.css';
|
|
10
10
|
import { useMemo, useState, useRef, useId, useEffect } from 'react';
|
|
11
11
|
import { ClockGlyph, ChevronDownGlyph, CheckGlyph, ChevronLeftGlyph, ChevronRightGlyph } from '@crewlethq/icons/glyphs';
|
|
12
12
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/utils/useMediaMatch.ts
|
|
4
|
+
function useMediaMatch(query) {
|
|
5
|
+
const subscribe = useCallback(
|
|
6
|
+
(notify) => {
|
|
7
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
|
|
8
|
+
};
|
|
9
|
+
const list = window.matchMedia(query);
|
|
10
|
+
list.addEventListener("change", notify);
|
|
11
|
+
return () => list.removeEventListener("change", notify);
|
|
12
|
+
},
|
|
13
|
+
[query]
|
|
14
|
+
);
|
|
15
|
+
const read = useCallback(
|
|
16
|
+
() => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(query).matches : false,
|
|
17
|
+
[query]
|
|
18
|
+
);
|
|
19
|
+
return useSyncExternalStore(subscribe, read, () => false);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { useMediaMatch };
|