@driftime/sanity-plugin-icon 0.1.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/dist/index.js ADDED
@@ -0,0 +1,685 @@
1
+ import { a as convertCase, i as serialiseIconNode, n as parseIconNode, o as isDefined, r as resolveIconNode, t as Drawing } from "./drawing-Bo0HTICv.js";
2
+ import { FormField, defineField, definePlugin, defineType, set, setIfMissing, unset } from "sanity";
3
+ import { c } from "react/compiler-runtime";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { CloseIcon } from "@sanity/icons/Close";
6
+ import { Box, Button, Dialog, Flex, Grid, Spinner, Stack, Text, TextInput } from "@sanity/ui";
7
+ import { createElement, useEffect, useRef, useState } from "react";
8
+ import { SearchIcon } from "@sanity/icons/Search";
9
+ import { Popover } from "@sanity/ui/popover";
10
+ function Field(t0) {
11
+ let $ = c(7), { title, description, inputId, validation, path, children } = t0, t1;
12
+ return $[0] !== children || $[1] !== description || $[2] !== inputId || $[3] !== path || $[4] !== title || $[5] !== validation ? (t1 = /* @__PURE__ */ jsx(FormField, {
13
+ title,
14
+ description,
15
+ inputId,
16
+ validation,
17
+ path,
18
+ children
19
+ }), $[0] = children, $[1] = description, $[2] = inputId, $[3] = path, $[4] = title, $[5] = validation, $[6] = t1) : t1 = $[6], t1;
20
+ }
21
+ /** Space between rows, in pixels. */
22
+ const gridLayoutStyle = {
23
+ justifyItems: "center",
24
+ rowGap: 4
25
+ };
26
+ function Cell(t0) {
27
+ let $ = c(24), focusable, icon, onSelect, position, props, selected, total;
28
+ $[0] === t0 ? (focusable = $[1], icon = $[2], onSelect = $[3], position = $[4], props = $[5], selected = $[6], total = $[7]) : ({icon, selected, focusable, position, total, onSelect, ...props} = t0, $[0] = t0, $[1] = focusable, $[2] = icon, $[3] = onSelect, $[4] = position, $[5] = props, $[6] = selected, $[7] = total);
29
+ let t1 = focusable ? 0 : -1, t2;
30
+ $[8] === Symbol.for("react.memo_cache_sentinel") ? (t2 = {
31
+ width: 40,
32
+ height: 40
33
+ }, $[8] = t2) : t2 = $[8];
34
+ let t3;
35
+ $[9] === icon.node ? t3 = $[10] : (t3 = /* @__PURE__ */ jsx(Drawing, {
36
+ node: icon.node,
37
+ width: "1.25em",
38
+ height: "1.25em",
39
+ strokeWidth: 1.5
40
+ }), $[9] = icon.node, $[10] = t3);
41
+ let t4;
42
+ $[11] !== icon || $[12] !== onSelect ? (t4 = () => {
43
+ onSelect(icon);
44
+ }, $[11] = icon, $[12] = onSelect, $[13] = t4) : t4 = $[13];
45
+ let t5;
46
+ return $[14] !== icon.label || $[15] !== icon.name || $[16] !== position || $[17] !== props || $[18] !== selected || $[19] !== t1 || $[20] !== t3 || $[21] !== t4 || $[22] !== total ? (t5 = /* @__PURE__ */ jsx(Button, {
47
+ type: "button",
48
+ role: "option",
49
+ "aria-selected": selected,
50
+ "aria-posinset": position,
51
+ "aria-setsize": total,
52
+ tabIndex: t1,
53
+ mode: "bleed",
54
+ selected,
55
+ padding: 0,
56
+ style: t2,
57
+ icon: t3,
58
+ "aria-label": icon.label,
59
+ "data-icon": icon.name,
60
+ onClick: t4,
61
+ ...props
62
+ }), $[14] = icon.label, $[15] = icon.name, $[16] = position, $[17] = props, $[18] = selected, $[19] = t1, $[20] = t3, $[21] = t4, $[22] = total, $[23] = t5) : t5 = $[23], t5;
63
+ }
64
+ function Recent(t0) {
65
+ let $ = c(23), icons, onForget, onSelect, props, selected;
66
+ $[0] === t0 ? (icons = $[1], onForget = $[2], onSelect = $[3], props = $[4], selected = $[5]) : ({icons, selected, onSelect, onForget, ...props} = t0, $[0] = t0, $[1] = icons, $[2] = onForget, $[3] = onSelect, $[4] = props, $[5] = selected);
67
+ let t1;
68
+ $[6] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(Text, {
69
+ size: 1,
70
+ weight: "medium",
71
+ muted: !0,
72
+ children: "Recent icons"
73
+ }), $[6] = t1) : t1 = $[6];
74
+ let t2;
75
+ $[7] === onForget ? t2 = $[8] : (t2 = /* @__PURE__ */ jsxs(Flex, {
76
+ align: "center",
77
+ justify: "space-between",
78
+ gap: 2,
79
+ children: [t1, /* @__PURE__ */ jsx(Button, {
80
+ type: "button",
81
+ mode: "bleed",
82
+ fontSize: 1,
83
+ padding: 2,
84
+ text: "Clear",
85
+ onClick: onForget,
86
+ "aria-label": "Forget the recently used icons."
87
+ })]
88
+ }), $[7] = onForget, $[8] = t2);
89
+ let t3;
90
+ if ($[9] !== icons || $[10] !== onSelect || $[11] !== selected) {
91
+ let t4;
92
+ $[13] !== icons.length || $[14] !== onSelect || $[15] !== selected ? (t4 = (icon, index) => /* @__PURE__ */ jsx(Cell, {
93
+ icon,
94
+ selected: icon.name === selected,
95
+ focusable: !0,
96
+ position: index + 1,
97
+ total: icons.length,
98
+ onSelect
99
+ }, icon.name), $[13] = icons.length, $[14] = onSelect, $[15] = selected, $[16] = t4) : t4 = $[16], t3 = icons.map(t4), $[9] = icons, $[10] = onSelect, $[11] = selected, $[12] = t3;
100
+ } else t3 = $[12];
101
+ let t4;
102
+ $[17] === t3 ? t4 = $[18] : (t4 = /* @__PURE__ */ jsx(Grid, {
103
+ role: "listbox",
104
+ "aria-label": "Recent icons",
105
+ gridTemplateColumns: 8,
106
+ style: gridLayoutStyle,
107
+ children: t3
108
+ }), $[17] = t3, $[18] = t4);
109
+ let t5;
110
+ return $[19] !== props || $[20] !== t2 || $[21] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
111
+ gap: 3,
112
+ ...props,
113
+ children: [t2, t4]
114
+ }), $[19] = props, $[20] = t2, $[21] = t4, $[22] = t5) : t5 = $[22], t5;
115
+ }
116
+ function Tooltip(t0) {
117
+ let $ = c(14), hovered, props;
118
+ $[0] === t0 ? (hovered = $[1], props = $[2]) : ({hovered, ...props} = t0, $[0] = t0, $[1] = hovered, $[2] = props);
119
+ let t1;
120
+ $[3] === hovered ? t1 = $[4] : (t1 = isDefined(hovered), $[3] = hovered, $[4] = t1);
121
+ let t2 = hovered?.element ?? null, t3, t4;
122
+ $[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = [
123
+ -4,
124
+ 0,
125
+ 0,
126
+ 0
127
+ ], t4 = { pointerEvents: "none" }, $[5] = t3, $[6] = t4) : (t3 = $[5], t4 = $[6]);
128
+ let t5;
129
+ $[7] === hovered ? t5 = $[8] : (t5 = isDefined(hovered) && /* @__PURE__ */ jsxs(Stack, {
130
+ gap: 3,
131
+ style: { maxWidth: "16rem" },
132
+ children: [/* @__PURE__ */ jsx(Text, {
133
+ size: 1,
134
+ weight: "medium",
135
+ children: hovered.icon.label
136
+ }), isDefined(hovered.icon.tags) && /* @__PURE__ */ jsx(Text, {
137
+ size: 1,
138
+ muted: !0,
139
+ children: hovered.icon.tags
140
+ })]
141
+ }), $[7] = hovered, $[8] = t5);
142
+ let t6;
143
+ return $[9] !== props || $[10] !== t1 || $[11] !== t2 || $[12] !== t5 ? (t6 = /* @__PURE__ */ jsx(Popover, {
144
+ open: t1,
145
+ referenceElement: t2,
146
+ placement: "top",
147
+ portal: !0,
148
+ padding: 3,
149
+ __unstable_margins: t3,
150
+ style: t4,
151
+ content: t5,
152
+ ...props
153
+ }), $[9] = props, $[10] = t1, $[11] = t2, $[12] = t5, $[13] = t6) : t6 = $[13], t6;
154
+ }
155
+ /** Properties every icon the Studio draws is rendered with. */
156
+ const defaultIconProps = {
157
+ width: "1em",
158
+ height: "1em",
159
+ strokeWidth: 1.5
160
+ };
161
+ /**
162
+ * Flattens text into the form a search compares against, so a hyphenated icon name and a multi-word
163
+ * tag are matched the same way.
164
+ *
165
+ * @param value - The text to flatten.
166
+ * @returns The text in comparable form.
167
+ */
168
+ function normaliseTerms(value) {
169
+ return value.toLowerCase().replaceAll(/[\s-]+/gu, " ").trim();
170
+ }
171
+ /**
172
+ * Reads the icon library, pairing each drawing with the terms it can be found by. Both files come
173
+ * from the same release as the icons themselves, so the drawings and their terms never disagree.
174
+ *
175
+ * @returns Every icon the library offers.
176
+ */
177
+ async function readLibrary() {
178
+ let [drawings, tags] = await Promise.all([import("lucide-static/icon-nodes.json"), import("lucide-static/tags.json")]), nodes = drawings.default, terms = tags.default;
179
+ return Object.keys(nodes).flatMap((name) => {
180
+ let node = parseIconNode(nodes[name]);
181
+ if (!isDefined(node)) return [];
182
+ let iconTags = terms[name] ?? [];
183
+ return [{
184
+ name,
185
+ label: convertCase(name, "sentence"),
186
+ tags: iconTags.join(", "),
187
+ node,
188
+ terms: normaliseTerms([name, ...iconTags].join(" "))
189
+ }];
190
+ });
191
+ }
192
+ /** The library once asked for, held so every field in a Studio session shares the one request. */
193
+ let libraryRequest;
194
+ /**
195
+ * Reads the icon library, sharing a single request across every field opened in the session. The
196
+ * library is a megabyte of data that never changes within a release, so asking twice only ever costs.
197
+ *
198
+ * @returns Every icon the library offers.
199
+ */
200
+ async function requestLibrary() {
201
+ return libraryRequest ??= readLibrary(), await libraryRequest;
202
+ }
203
+ /**
204
+ * Reads the icon library, sharing one request with every other field in the session. Callers mount
205
+ * only while a picker is open, so a Studio that never opens one never pays the megabyte it costs.
206
+ *
207
+ * @returns Every icon the library offers, or undefined while it is still being read.
208
+ */
209
+ function useIconLibrary() {
210
+ let $ = c(2), [library, setLibrary] = useState(), t0, t1;
211
+ return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
212
+ let active = !0;
213
+ return (async function load() {
214
+ let loaded = await requestLibrary();
215
+ active && setLibrary(loaded);
216
+ })(), () => {
217
+ active = !1;
218
+ };
219
+ }, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1), library;
220
+ }
221
+ /** Where the icons an author reached for last are kept, in the browser rather than the dataset. */
222
+ const storageKey = "driftime:sanity-plugin-icon:recent";
223
+ /**
224
+ * Reads the icons an author reached for last.
225
+ *
226
+ * @returns Names of the remembered icons, most recent first.
227
+ */
228
+ function readRecent() {
229
+ try {
230
+ let stored = JSON.parse(globalThis.localStorage.getItem(storageKey) ?? "[]");
231
+ return Array.isArray(stored) ? stored.filter((entry) => typeof entry == "string") : [];
232
+ } catch {
233
+ return [];
234
+ }
235
+ }
236
+ /**
237
+ * Remembers an icon as the one reached for most recently.
238
+ *
239
+ * @param name - Name of the chosen icon.
240
+ * @returns Names of the remembered icons, most recent first.
241
+ */
242
+ function writeRecent(name) {
243
+ let updated = [name, ...readRecent().filter((entry) => entry !== name)].slice(0, 8);
244
+ try {
245
+ globalThis.localStorage.setItem(storageKey, JSON.stringify(updated));
246
+ } catch {}
247
+ return updated;
248
+ }
249
+ /** Forgets every icon reached for so far. */
250
+ function clearRecent() {
251
+ try {
252
+ globalThis.localStorage.removeItem(storageKey);
253
+ } catch {}
254
+ }
255
+ /**
256
+ * Tracks the icons an author reached for last, keeping what is remembered and what is drawn in step.
257
+ * The stored list is read as the picker mounts, so another tab choosing an icon is picked up too.
258
+ *
259
+ * @returns The remembered names, most recent first, alongside ways to add to and empty them.
260
+ */
261
+ function useRecentIcons() {
262
+ let $ = c(4), [recent, setRecent] = useState(readRecent), t0;
263
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = function remember(name) {
264
+ setRecent(writeRecent(name));
265
+ }, $[0] = t0) : t0 = $[0];
266
+ let remember = t0, t1;
267
+ $[1] === Symbol.for("react.memo_cache_sentinel") ? (t1 = function forget() {
268
+ clearRecent(), setRecent([]);
269
+ }, $[1] = t1) : t1 = $[1];
270
+ let forget = t1, t2;
271
+ return $[2] === recent ? t2 = $[3] : (t2 = {
272
+ recent,
273
+ remember,
274
+ forget
275
+ }, $[2] = recent, $[3] = t2), t2;
276
+ }
277
+ /**
278
+ * Works out which icon a key press moves to, so the library can be walked without a mouse.
279
+ *
280
+ * @param key - The key pressed.
281
+ * @param current - Index of the icon the grid is on.
282
+ * @param last - Index of the final icon in the results.
283
+ * @returns The index moved to, or undefined when the key does not move.
284
+ */
285
+ function getNextIndex(key, current, last) {
286
+ if (key === "ArrowRight") return current + 1;
287
+ if (key === "ArrowLeft") return current - 1;
288
+ if (key === "ArrowDown") return current + 8;
289
+ if (key === "ArrowUp") return current - 8;
290
+ if (key === "Home") return 0;
291
+ if (key === "End") return last;
292
+ }
293
+ /**
294
+ * Works out which rows the grid builds for a given scroll position, so only the icons near the
295
+ * viewport are mounted. The rows above and below stand in as padding, keeping the scrollbar honest.
296
+ *
297
+ * @param scrollRow - Row the scrolling area has reached.
298
+ * @param count - Number of icons in the results.
299
+ * @returns The row the grid starts building at, the row it stops at, and the row count in total.
300
+ */
301
+ function getVisibleRows(scrollRow, count) {
302
+ let totalRows = Math.ceil(count / 8), firstRow = Math.max(0, scrollRow - 8);
303
+ return {
304
+ totalRows,
305
+ firstRow,
306
+ lastRow: Math.min(totalRows, firstRow + 8 + 16)
307
+ };
308
+ }
309
+ function Picker(t0) {
310
+ let $ = c(28), onSelect, props, selected;
311
+ $[0] === t0 ? (onSelect = $[1], props = $[2], selected = $[3]) : ({selected, onSelect, ...props} = t0, $[0] = t0, $[1] = onSelect, $[2] = props, $[3] = selected);
312
+ let library = useIconLibrary(), { recent, remember, forget } = useRecentIcons(), [search, setSearch] = useState(""), [scrollRow, setScrollRow] = useState(0), [movedIndex, setMovedIndex] = useState(), [hovered, setHovered] = useState(), [scrolling, setScrolling] = useState(!1), scroller = useRef(null), activeCell = useRef(null), focusActiveCell = useRef(!1), scrollFrame = useRef(void 0), scrollStop = useRef(void 0), hoverStart = useRef(void 0), query = normaliseTerms(search), searching = isDefined(query), results = (library ?? []).filter((icon) => !searching || icon.terms.includes(query)), byName = new Map((library ?? []).map(_temp)), recentIcons = searching ? [] : recent.flatMap((entry) => {
313
+ let icon_1 = byName.get(entry);
314
+ return isDefined(icon_1) ? [icon_1] : [];
315
+ }), { totalRows, firstRow, lastRow } = getVisibleRows(scrollRow, results.length), visible = results.slice(firstRow * 8, lastRow * 8), selectedIndex = searching || !isDefined(library) || !isDefined(selected) ? -1 : library.findIndex((icon_2) => icon_2.name === selected), activeIndex = movedIndex ?? Math.max(0, selectedIndex), t1, t2;
316
+ $[4] === selectedIndex ? (t1 = $[5], t2 = $[6]) : (t1 = () => {
317
+ let element = scroller.current;
318
+ isDefined(element) && selectedIndex !== -1 && (element.scrollTop = Math.max(0, Math.floor(selectedIndex / 8) * 44 - 174));
319
+ }, t2 = [selectedIndex], $[4] = selectedIndex, $[5] = t1, $[6] = t2), useEffect(t1, t2);
320
+ let t3, t4;
321
+ $[7] === Symbol.for("react.memo_cache_sentinel") ? (t3 = () => () => {
322
+ isDefined(scrollFrame.current) && cancelAnimationFrame(scrollFrame.current), isDefined(scrollStop.current) && clearTimeout(scrollStop.current), isDefined(hoverStart.current) && clearTimeout(hoverStart.current);
323
+ }, t4 = [], $[7] = t3, $[8] = t4) : (t3 = $[7], t4 = $[8]), useEffect(t3, t4);
324
+ let t5;
325
+ $[9] === Symbol.for("react.memo_cache_sentinel") ? (t5 = () => {
326
+ focusActiveCell.current && (focusActiveCell.current = !1, isDefined(activeCell.current) && activeCell.current.focus());
327
+ }, $[9] = t5) : t5 = $[9];
328
+ let t6;
329
+ $[10] === activeIndex ? t6 = $[11] : (t6 = [activeIndex], $[10] = activeIndex, $[11] = t6), useEffect(t5, t6);
330
+ let t7;
331
+ $[12] !== onSelect || $[13] !== remember ? (t7 = function handleSelect(icon_3) {
332
+ remember(icon_3.name), onSelect(icon_3);
333
+ }, $[12] = onSelect, $[13] = remember, $[14] = t7) : t7 = $[14];
334
+ let handleSelect = t7, t8;
335
+ $[15] === Symbol.for("react.memo_cache_sentinel") ? (t8 = function handleSearch(event) {
336
+ setSearch(event.currentTarget.value), setMovedIndex(void 0), isDefined(scroller.current) && (scroller.current.scrollTop = 0);
337
+ }, $[15] = t8) : t8 = $[15];
338
+ let handleSearch = t8, selectActive = function selectActive() {
339
+ let icon_4 = results[activeIndex];
340
+ isDefined(icon_4) && handleSelect(icon_4);
341
+ }, handleSearchKeyDown = function handleSearchKeyDown(event_0) {
342
+ if (event_0.key === "Enter") {
343
+ event_0.preventDefault(), selectActive();
344
+ return;
345
+ }
346
+ event_0.key === "ArrowDown" && (event_0.preventDefault(), isDefined(activeCell.current) && activeCell.current.focus());
347
+ }, t9;
348
+ $[16] === scrolling ? t9 = $[17] : (t9 = function handleScroll() {
349
+ isDefined(hoverStart.current) && clearTimeout(hoverStart.current), setHovered(void 0), scrolling || setScrolling(!0), isDefined(scrollStop.current) && clearTimeout(scrollStop.current), scrollStop.current = setTimeout(() => {
350
+ setScrolling(!1);
351
+ }, 150), !isDefined(scrollFrame.current) && (scrollFrame.current = requestAnimationFrame(() => {
352
+ scrollFrame.current = void 0;
353
+ let element_0 = scroller.current;
354
+ isDefined(element_0) && setScrollRow(Math.floor(element_0.scrollTop / 44));
355
+ }));
356
+ }, $[16] = scrolling, $[17] = t9);
357
+ let handleScroll = t9, handleHover = function handleHover(event_1) {
358
+ if (scrolling) return;
359
+ let target = event_1.target instanceof Element ? event_1.target.closest("[data-icon]") : void 0, cell = target instanceof HTMLElement ? target : void 0;
360
+ if (cell === hovered?.element) return;
361
+ let name = cell?.dataset.icon, icon_5 = isDefined(name) ? byName.get(name) : void 0;
362
+ if (isDefined(hoverStart.current) && clearTimeout(hoverStart.current), !isDefined(icon_5) || !isDefined(cell)) {
363
+ setHovered(void 0);
364
+ return;
365
+ }
366
+ if (isDefined(hovered)) {
367
+ setHovered({
368
+ icon: icon_5,
369
+ element: cell
370
+ });
371
+ return;
372
+ }
373
+ hoverStart.current = setTimeout(() => {
374
+ setHovered({
375
+ icon: icon_5,
376
+ element: cell
377
+ });
378
+ }, 200);
379
+ }, t10;
380
+ $[18] === hovered ? t10 = $[19] : (t10 = function clearHover() {
381
+ isDefined(hoverStart.current) && clearTimeout(hoverStart.current), isDefined(hovered) && setHovered(void 0);
382
+ }, $[18] = hovered, $[19] = t10);
383
+ let clearHover = t10, handleGridKeyDown = function handleGridKeyDown(event_2) {
384
+ let last = results.length - 1;
385
+ if (last < 0) return;
386
+ if (event_2.key === "Enter" || event_2.key === " ") {
387
+ event_2.preventDefault(), selectActive();
388
+ return;
389
+ }
390
+ let next = getNextIndex(event_2.key, activeIndex, last);
391
+ if (!isDefined(next)) return;
392
+ event_2.preventDefault();
393
+ let index = Math.min(Math.max(next, 0), last);
394
+ focusActiveCell.current = !0, setMovedIndex(index);
395
+ let element_1 = scroller.current;
396
+ if (!isDefined(element_1)) return;
397
+ let top = Math.floor(index / 8) * 44;
398
+ top < element_1.scrollTop ? element_1.scrollTop = top : top + 44 > element_1.scrollTop + 348 && (element_1.scrollTop = top + 44 - 348);
399
+ }, t11;
400
+ $[20] === Symbol.for("react.memo_cache_sentinel") ? (t11 = /* @__PURE__ */ jsx(SearchIcon, { ...defaultIconProps }), $[20] = t11) : t11 = $[20];
401
+ let t12;
402
+ $[21] === library ? t12 = $[22] : (t12 = isDefined(library), $[21] = library, $[22] = t12);
403
+ let t13;
404
+ $[23] === library ? t13 = $[24] : (t13 = !isDefined(library) && /* @__PURE__ */ jsx(Flex, {
405
+ align: "center",
406
+ justify: "center",
407
+ style: { height: 348 },
408
+ children: /* @__PURE__ */ jsx(Spinner, { muted: !0 })
409
+ }), $[23] = library, $[24] = t13);
410
+ let t14 = hovered?.icon.name, t15;
411
+ return $[25] !== hovered || $[26] !== t14 ? (t15 = /* @__PURE__ */ jsx(Tooltip, { hovered }, t14), $[25] = hovered, $[26] = t14, $[27] = t15) : t15 = $[27], /* @__PURE__ */ jsx(Dialog, {
412
+ header: "Select icon",
413
+ width: 1,
414
+ ...props,
415
+ children: /* @__PURE__ */ jsxs(Stack, {
416
+ gap: 4,
417
+ padding: 4,
418
+ children: [
419
+ /* @__PURE__ */ jsx(TextInput, {
420
+ icon: t11,
421
+ placeholder: "Search by name or by what the icon depicts",
422
+ value: search,
423
+ onChange: handleSearch,
424
+ onKeyDown: handleSearchKeyDown,
425
+ disabled: !t12,
426
+ "aria-label": "Search the icon library"
427
+ }),
428
+ t13,
429
+ isDefined(recentIcons) && /* @__PURE__ */ jsx(Recent, {
430
+ icons: recentIcons,
431
+ selected,
432
+ onSelect: handleSelect,
433
+ onForget: forget,
434
+ onMouseOver: handleHover,
435
+ onMouseLeave: clearHover
436
+ }),
437
+ isDefined(library) && /* @__PURE__ */ jsxs(Stack, {
438
+ gap: 3,
439
+ children: [/* @__PURE__ */ jsx(Box, {
440
+ paddingY: 2,
441
+ children: /* @__PURE__ */ jsxs(Text, {
442
+ size: 1,
443
+ weight: "medium",
444
+ muted: !0,
445
+ children: [
446
+ "All icons (",
447
+ results.length.toLocaleString("en-GB"),
448
+ ")"
449
+ ]
450
+ })
451
+ }), /* @__PURE__ */ jsxs(Box, {
452
+ ref: scroller,
453
+ role: "listbox",
454
+ "aria-label": "Icon library",
455
+ onScroll: handleScroll,
456
+ onMouseOver: handleHover,
457
+ onMouseLeave: clearHover,
458
+ onKeyDown: handleGridKeyDown,
459
+ style: {
460
+ height: 348,
461
+ overflowY: "auto"
462
+ },
463
+ children: [results.length === 0 && /* @__PURE__ */ jsxs(Flex, {
464
+ align: "center",
465
+ justify: "center",
466
+ direction: "column",
467
+ gap: 3,
468
+ style: { height: 348 },
469
+ children: [/* @__PURE__ */ jsxs(Text, {
470
+ size: 1,
471
+ muted: !0,
472
+ children: [
473
+ "Nothing matches “",
474
+ search,
475
+ "”."
476
+ ]
477
+ }), /* @__PURE__ */ jsx(Button, {
478
+ type: "button",
479
+ mode: "ghost",
480
+ text: "Clear search",
481
+ onClick: () => {
482
+ setSearch(""), setMovedIndex(void 0);
483
+ }
484
+ })]
485
+ }), results.length > 0 && /* @__PURE__ */ jsx(Grid, {
486
+ gridTemplateColumns: 8,
487
+ style: {
488
+ ...gridLayoutStyle,
489
+ paddingTop: firstRow * 44,
490
+ paddingBottom: (totalRows - lastRow) * 44
491
+ },
492
+ children: visible.map((icon_6, offset) => {
493
+ let index_0 = firstRow * 8 + offset;
494
+ return /* @__PURE__ */ jsx(Cell, {
495
+ ref: index_0 === activeIndex ? activeCell : void 0,
496
+ icon: icon_6,
497
+ selected: icon_6.name === selected,
498
+ focusable: index_0 === activeIndex,
499
+ position: index_0 + 1,
500
+ total: results.length,
501
+ onSelect: handleSelect
502
+ }, icon_6.name);
503
+ })
504
+ })]
505
+ })]
506
+ }),
507
+ t15
508
+ ]
509
+ })
510
+ });
511
+ }
512
+ function _temp(icon_0) {
513
+ return [icon_0.name, icon_0];
514
+ }
515
+ /** Type name of the icon object. */
516
+ const iconTypeName = "icon";
517
+ function Input(t0) {
518
+ let $ = c(28), { id, value, onChange, readOnly } = t0, { name, node } = value ?? {}, [open, setOpen] = useState(!1), selectedNode = resolveIconNode(node), selectedLabel = isDefined(name) ? convertCase(name, "sentence") : void 0, t1;
519
+ $[0] !== onChange || $[1] !== setOpen ? (t1 = function handleSelect(icon) {
520
+ onChange([
521
+ setIfMissing({ _type: iconTypeName }),
522
+ set(icon.name, ["name"]),
523
+ set(serialiseIconNode(icon.node), ["node"])
524
+ ]), setOpen(!1);
525
+ }, $[0] = onChange, $[1] = setOpen, $[2] = t1) : t1 = $[2];
526
+ let handleSelect = t1, T0 = Flex, T1 = Button, t5 = isDefined(selectedNode) ? /* @__PURE__ */ jsx(Drawing, {
527
+ node: selectedNode,
528
+ ...defaultIconProps
529
+ }) : void 0, t6 = selectedLabel ?? "Select icon", t7;
530
+ $[3] === setOpen ? t7 = $[4] : (t7 = () => {
531
+ setOpen(!0);
532
+ }, $[3] = setOpen, $[4] = t7);
533
+ let t8 = isDefined(selectedLabel) ? `${selectedLabel} currently selected` : "Select icon", t9;
534
+ $[5] !== T1 || $[6] !== id || $[7] !== readOnly || $[8] !== t5 || $[9] !== t6 || $[10] !== t7 || $[11] !== t8 ? (t9 = /* @__PURE__ */ jsx(T1, {
535
+ id,
536
+ type: "button",
537
+ mode: "ghost",
538
+ icon: t5,
539
+ text: t6,
540
+ disabled: readOnly,
541
+ onClick: t7,
542
+ "aria-label": t8
543
+ }), $[5] = T1, $[6] = id, $[7] = readOnly, $[8] = t5, $[9] = t6, $[10] = t7, $[11] = t8, $[12] = t9) : t9 = $[12];
544
+ let t10;
545
+ $[13] !== onChange || $[14] !== readOnly || $[15] !== value ? (t10 = isDefined(value) && /* @__PURE__ */ jsx(Button, {
546
+ type: "button",
547
+ mode: "ghost",
548
+ tone: "critical",
549
+ icon: /* @__PURE__ */ jsx(CloseIcon, { ...defaultIconProps }),
550
+ text: "Clear",
551
+ disabled: readOnly,
552
+ onClick: () => {
553
+ onChange(unset());
554
+ },
555
+ "aria-label": "Clear the currently selected icon."
556
+ }), $[13] = onChange, $[14] = readOnly, $[15] = value, $[16] = t10) : t10 = $[16];
557
+ let t11;
558
+ $[17] !== handleSelect || $[18] !== id || $[19] !== name || $[20] !== open || $[21] !== setOpen ? (t11 = open && /* @__PURE__ */ jsx(Picker, {
559
+ id: `${id}-library`,
560
+ selected: name,
561
+ onSelect: handleSelect,
562
+ onClose: () => {
563
+ setOpen(!1);
564
+ },
565
+ onClickOutside: () => {
566
+ setOpen(!1);
567
+ }
568
+ }), $[17] = handleSelect, $[18] = id, $[19] = name, $[20] = open, $[21] = setOpen, $[22] = t11) : t11 = $[22];
569
+ let t12;
570
+ return $[23] !== T0 || $[24] !== t10 || $[25] !== t11 || $[26] !== t9 ? (t12 = /* @__PURE__ */ jsxs(T0, {
571
+ gap: 2,
572
+ children: [
573
+ t9,
574
+ t10,
575
+ t11
576
+ ]
577
+ }), $[23] = T0, $[24] = t10, $[25] = t11, $[26] = t9, $[27] = t12) : t12 = $[27], t12;
578
+ }
579
+ /**
580
+ * Builds the media a stored icon previews with, so a document lists the drawing an author chose
581
+ * rather than one placeholder standing in for every icon alike.
582
+ *
583
+ * @param value - The stored icon.
584
+ * @returns The media component, or undefined when nothing readable was stored.
585
+ * @public
586
+ */
587
+ function createIconPreview(value) {
588
+ let node = resolveIconNode(value?.node);
589
+ if (isDefined(node)) return function IconPreview() {
590
+ return /* @__PURE__ */ jsx(Drawing, {
591
+ node,
592
+ ...defaultIconProps
593
+ });
594
+ };
595
+ }
596
+ function SquareDashedIcon(props) {
597
+ let $ = c(14), t0, t1, t10, t11, t2, t3, t4, t5, t6, t7, t8, t9;
598
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "M5 3a2 2 0 0 0-2 2" }), t1 = /* @__PURE__ */ jsx("path", { d: "M19 3a2 2 0 0 1 2 2" }), t2 = /* @__PURE__ */ jsx("path", { d: "M21 19a2 2 0 0 1-2 2" }), t3 = /* @__PURE__ */ jsx("path", { d: "M5 21a2 2 0 0 1-2-2" }), t4 = /* @__PURE__ */ jsx("path", { d: "M9 3h1" }), t5 = /* @__PURE__ */ jsx("path", { d: "M9 21h1" }), t6 = /* @__PURE__ */ jsx("path", { d: "M14 3h1" }), t7 = /* @__PURE__ */ jsx("path", { d: "M14 21h1" }), t8 = /* @__PURE__ */ jsx("path", { d: "M3 9v1" }), t9 = /* @__PURE__ */ jsx("path", { d: "M21 9v1" }), t10 = /* @__PURE__ */ jsx("path", { d: "M3 14v1" }), t11 = /* @__PURE__ */ jsx("path", { d: "M21 14v1" }), $[0] = t0, $[1] = t1, $[2] = t10, $[3] = t11, $[4] = t2, $[5] = t3, $[6] = t4, $[7] = t5, $[8] = t6, $[9] = t7, $[10] = t8, $[11] = t9) : (t0 = $[0], t1 = $[1], t10 = $[2], t11 = $[3], t2 = $[4], t3 = $[5], t4 = $[6], t5 = $[7], t6 = $[8], t7 = $[9], t8 = $[10], t9 = $[11]);
599
+ let t12;
600
+ return $[12] === props ? t12 = $[13] : (t12 = /* @__PURE__ */ jsxs("svg", {
601
+ xmlns: "http://www.w3.org/2000/svg",
602
+ width: 24,
603
+ height: 24,
604
+ viewBox: "0 0 24 24",
605
+ fill: "none",
606
+ stroke: "currentColor",
607
+ strokeWidth: 2,
608
+ strokeLinecap: "round",
609
+ strokeLinejoin: "round",
610
+ ...props,
611
+ children: [
612
+ t0,
613
+ t1,
614
+ t2,
615
+ t3,
616
+ t4,
617
+ t5,
618
+ t6,
619
+ t7,
620
+ t8,
621
+ t9,
622
+ t10,
623
+ t11
624
+ ]
625
+ }), $[12] = props, $[13] = t12), t12;
626
+ }
627
+ /**
628
+ * Creates a Sanity Studio icon component from an SVG icon.
629
+ *
630
+ * @param icon - The icon component to wrap.
631
+ * @param props - Additional props merged over the default properties a Studio icon is drawn with.
632
+ * @returns A Studio icon component.
633
+ */
634
+ function createSanityIcon(icon, props) {
635
+ let Icon = icon;
636
+ function SanityIcon() {
637
+ return createElement(Icon, {
638
+ ...defaultIconProps,
639
+ ...props
640
+ });
641
+ }
642
+ return SanityIcon;
643
+ }
644
+ const iconType = defineType({
645
+ name: iconTypeName,
646
+ type: "object",
647
+ icon: createSanityIcon(SquareDashedIcon),
648
+ description: "Icon chosen from the Lucide library.",
649
+ components: {
650
+ field: Field,
651
+ input: Input
652
+ },
653
+ preview: {
654
+ select: {
655
+ name: "name",
656
+ node: "node"
657
+ },
658
+ prepare(selection) {
659
+ let { name, node } = selection;
660
+ return {
661
+ title: isDefined(name) ? convertCase(name, "sentence") : "No icon",
662
+ media: createIconPreview({
663
+ _type: iconTypeName,
664
+ name,
665
+ node
666
+ })
667
+ };
668
+ }
669
+ },
670
+ fields: [defineField({
671
+ name: "name",
672
+ type: "string",
673
+ description: "Name of the chosen icon in the library it was taken from."
674
+ }), defineField({
675
+ name: "node",
676
+ type: "text",
677
+ description: "Shapes the icon is drawn from, written when the icon is chosen."
678
+ })]
679
+ }), iconPlugin = definePlugin({
680
+ name: "@driftime/sanity-plugin-icon",
681
+ schema: { types: [iconType] }
682
+ });
683
+ export { createIconPreview, iconPlugin };
684
+
685
+ //# sourceMappingURL=index.js.map