@driftime/sanity-plugin-icon 0.2.0 → 0.4.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.
Files changed (56) hide show
  1. package/README.md +341 -113
  2. package/dist/adapters-Bx_MyEX_.js +633 -0
  3. package/dist/adapters-Bx_MyEX_.js.map +1 -0
  4. package/dist/drawing-ByrcBbWB.js +50 -0
  5. package/dist/drawing-ByrcBbWB.js.map +1 -0
  6. package/dist/index.d.ts +85 -33
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +746 -443
  9. package/dist/index.js.map +1 -1
  10. package/dist/names.d.ts +8 -0
  11. package/dist/names.d.ts.map +1 -0
  12. package/dist/names.js +0 -0
  13. package/dist/next-loader.d.ts +64 -0
  14. package/dist/next-loader.d.ts.map +1 -0
  15. package/dist/next-loader.js +102 -0
  16. package/dist/next-loader.js.map +1 -0
  17. package/dist/next.d.ts +43 -0
  18. package/dist/next.d.ts.map +1 -0
  19. package/dist/next.js +51 -0
  20. package/dist/next.js.map +1 -0
  21. package/dist/nodes-DAzj56Xn.js +139 -0
  22. package/dist/nodes-DAzj56Xn.js.map +1 -0
  23. package/dist/read-worker.d.ts +1 -0
  24. package/dist/read-worker.js +23 -0
  25. package/dist/read-worker.js.map +1 -0
  26. package/dist/registry-BEEpqK3z.js +13 -0
  27. package/dist/registry-BEEpqK3z.js.map +1 -0
  28. package/dist/registry-dxVzo7ZX.js +58 -0
  29. package/dist/registry-dxVzo7ZX.js.map +1 -0
  30. package/dist/render.d.ts +8 -9
  31. package/dist/render.d.ts.map +1 -1
  32. package/dist/render.js +5 -6
  33. package/dist/render.js.map +1 -1
  34. package/dist/sets-DVh8eYeP.js +258 -0
  35. package/dist/sets-DVh8eYeP.js.map +1 -0
  36. package/dist/sets-kepuObH8.js +90 -0
  37. package/dist/sets-kepuObH8.js.map +1 -0
  38. package/dist/sets.d.ts +50 -0
  39. package/dist/sets.d.ts.map +1 -0
  40. package/dist/sets.js +14 -0
  41. package/dist/sets.js.map +1 -0
  42. package/dist/types-qlHEYDoN.d.ts +20 -0
  43. package/dist/types-qlHEYDoN.d.ts.map +1 -0
  44. package/dist/utils-COPacfAq.js +48 -0
  45. package/dist/utils-COPacfAq.js.map +1 -0
  46. package/dist/vite-ifICG8y9.d.ts +98 -0
  47. package/dist/vite-ifICG8y9.d.ts.map +1 -0
  48. package/dist/vite.d.ts +2 -0
  49. package/dist/vite.js +88 -0
  50. package/dist/vite.js.map +1 -0
  51. package/package.json +58 -30
  52. package/LICENSE +0 -21
  53. package/dist/drawing-Bo0HTICv.js +0 -120
  54. package/dist/drawing-Bo0HTICv.js.map +0 -1
  55. package/dist/types-_D2jGm25.d.ts +0 -16
  56. package/dist/types-_D2jGm25.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,49 +1,148 @@
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";
1
+ import { r as pluginName, t as parseIconSet } from "./sets-kepuObH8.js";
2
+ import { n as isDefined, t as convertCase } from "./utils-COPacfAq.js";
3
+ import { a as resolveIconDrawing, o as serializeIconDrawing } from "./nodes-DAzj56Xn.js";
4
+ import { t as Drawing } from "./drawing-ByrcBbWB.js";
5
+ import { t as getIconSetKey } from "./registry-BEEpqK3z.js";
6
+ import { ChangeIndicator, FieldActionsProvider, FieldActionsResolver, FormField, defineField, definePlugin, defineType, getPublishedId, set, setIfMissing, unset, useFormValue } from "sanity";
3
7
  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
8
  import { createElement, useEffect, useRef, useState } from "react";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { CloseIcon } from "@sanity/icons/Close";
11
+ import { Box, Button, Card, Dialog, Flex, Grid, Spinner, Stack, Text, TextInput } from "@sanity/ui";
8
12
  import { SearchIcon } from "@sanity/icons/Search";
13
+ import { WarningOutlineIcon } from "@sanity/icons/WarningOutline";
9
14
  import { Popover } from "@sanity/ui/popover";
15
+ import { iconRegistry } from "@driftime/sanity-plugin-icon/sets";
16
+ /**
17
+ * Checks whether a form node has members, as objects and arrays of objects do.
18
+ *
19
+ * @param node - The form node to check.
20
+ * @returns True if the node has members.
21
+ */
22
+ function hasMembers(node) {
23
+ return "members" in node;
24
+ }
25
+ /**
26
+ * Collects the validation on a form node and everything inside it.
27
+ *
28
+ * @param node - The form node.
29
+ * @returns Every validation result on the node or inside it.
30
+ */
31
+ function collectNodeValidation(node) {
32
+ return [...node.validation, ...hasMembers(node) ? collectMemberValidation(node.members) : []];
33
+ }
34
+ /**
35
+ * Collects the validation anywhere inside a field's members. The form only passes a field the
36
+ * validation at its own path, so a field that hides its members would otherwise show none of theirs.
37
+ *
38
+ * @param members - The members.
39
+ * @returns Every validation result on the members or inside them.
40
+ */
41
+ function collectMemberValidation(members) {
42
+ return members.flatMap((member) => member.kind === "field" ? collectNodeValidation(member.field) : member.kind === "fieldSet" ? collectMemberValidation(member.fieldSet.members) : member.kind === "item" ? collectNodeValidation(member.item) : []);
43
+ }
10
44
  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,
45
+ let $ = c(40), { actions, schemaType, title, description, presence, validation, level, inputId, path, changed, children, inputProps } = t0, t1;
46
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = [], $[0] = t1) : t1 = $[0];
47
+ let [headerActions, setHeaderActions] = useState(t1), t2;
48
+ $[1] === Symbol.for("react.memo_cache_sentinel") ? (t2 = ["_id"], $[1] = t2) : t2 = $[1];
49
+ let documentId = useFormValue(t2), fieldValidation, focused, t3;
50
+ if ($[2] !== actions || $[3] !== documentId || $[4] !== inputProps.focused || $[5] !== inputProps.members || $[6] !== path || $[7] !== schemaType || $[8] !== validation) {
51
+ let publishedId = typeof documentId == "string" ? getPublishedId(documentId) : void 0;
52
+ focused = inputProps.focused === !0;
53
+ let t4;
54
+ $[12] !== inputProps.members || $[13] !== validation ? (t4 = [...validation, ...collectMemberValidation(inputProps.members)], $[12] = inputProps.members, $[13] = validation, $[14] = t4) : t4 = $[14], fieldValidation = t4, t3 = isDefined(publishedId) && isDefined(actions) && /* @__PURE__ */ jsx(FieldActionsResolver, {
55
+ actions,
56
+ documentId: publishedId,
57
+ documentType: schemaType.name,
58
+ onActions: setHeaderActions,
59
+ path,
60
+ schemaType
61
+ }), $[2] = actions, $[3] = documentId, $[4] = inputProps.focused, $[5] = inputProps.members, $[6] = path, $[7] = schemaType, $[8] = validation, $[9] = fieldValidation, $[10] = focused, $[11] = t3;
62
+ } else fieldValidation = $[9], focused = $[10], t3 = $[11];
63
+ let t4;
64
+ $[15] !== changed || $[16] !== children || $[17] !== focused || $[18] !== path ? (t4 = /* @__PURE__ */ jsx(ChangeIndicator, {
65
+ path,
66
+ hasFocus: focused,
67
+ isChanged: changed,
68
+ children
69
+ }), $[15] = changed, $[16] = children, $[17] = focused, $[18] = path, $[19] = t4) : t4 = $[19];
70
+ let t5;
71
+ $[20] !== description || $[21] !== fieldValidation || $[22] !== headerActions || $[23] !== inputId || $[24] !== inputProps.readOnly || $[25] !== level || $[26] !== path || $[27] !== presence || $[28] !== schemaType.deprecated || $[29] !== t4 || $[30] !== title ? (t5 = /* @__PURE__ */ jsx(FormField, {
72
+ __unstable_headerActions: headerActions,
73
+ __unstable_presence: presence,
14
74
  description,
15
75
  inputId,
16
- validation,
76
+ level,
77
+ title,
78
+ validation: fieldValidation,
79
+ deprecated: schemaType.deprecated,
17
80
  path,
18
- children
19
- }), $[0] = children, $[1] = description, $[2] = inputId, $[3] = path, $[4] = title, $[5] = validation, $[6] = t1) : t1 = $[6], t1;
81
+ readOnly: inputProps.readOnly,
82
+ children: t4
83
+ }), $[20] = description, $[21] = fieldValidation, $[22] = headerActions, $[23] = inputId, $[24] = inputProps.readOnly, $[25] = level, $[26] = path, $[27] = presence, $[28] = schemaType.deprecated, $[29] = t4, $[30] = title, $[31] = t5) : t5 = $[31];
84
+ let t6;
85
+ $[32] !== focused || $[33] !== headerActions || $[34] !== path || $[35] !== t5 ? (t6 = /* @__PURE__ */ jsx(FieldActionsProvider, {
86
+ actions: headerActions,
87
+ focused,
88
+ path,
89
+ children: t5
90
+ }), $[32] = focused, $[33] = headerActions, $[34] = path, $[35] = t5, $[36] = t6) : t6 = $[36];
91
+ let t7;
92
+ return $[37] !== t3 || $[38] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Fragment, { children: [t3, t6] }), $[37] = t3, $[38] = t6, $[39] = t7) : t7 = $[39], t7;
93
+ }
94
+ /** Default props for the plugin's own icons. Icons from `@sanity/icons` already match the Studio. */
95
+ const defaultIconProps = {
96
+ width: "1em",
97
+ height: "1em",
98
+ strokeWidth: 1.5
99
+ };
100
+ /**
101
+ * Wraps an SVG icon component for use in the Studio.
102
+ *
103
+ * @param icon - The icon to wrap.
104
+ * @param props - Props that override the Studio icon defaults.
105
+ * @returns An icon component.
106
+ */
107
+ function createSanityIcon(icon, props) {
108
+ let Icon = icon;
109
+ function SanityIcon() {
110
+ return createElement(Icon, {
111
+ ...defaultIconProps,
112
+ ...props
113
+ });
114
+ }
115
+ return SanityIcon;
20
116
  }
21
- /** Space between rows, in pixels. */
117
+ /** Gap between rows in pixels. */
22
118
  const gridLayoutStyle = {
23
119
  justifyItems: "center",
24
120
  rowGap: 4
25
121
  };
26
122
  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);
123
+ let $ = c(28), focusable, icon, onHover, onSelect, position, props, selected, total;
124
+ $[0] === t0 ? (focusable = $[1], icon = $[2], onHover = $[3], onSelect = $[4], position = $[5], props = $[6], selected = $[7], total = $[8]) : ({icon, selected, focusable, position, total, onSelect, onHover, ...props} = t0, $[0] = t0, $[1] = focusable, $[2] = icon, $[3] = onHover, $[4] = onSelect, $[5] = position, $[6] = props, $[7] = selected, $[8] = total);
29
125
  let t1 = focusable ? 0 : -1, t2;
30
- $[8] === Symbol.for("react.memo_cache_sentinel") ? (t2 = {
126
+ $[9] === Symbol.for("react.memo_cache_sentinel") ? (t2 = {
31
127
  width: 40,
32
128
  height: 40
33
- }, $[8] = t2) : t2 = $[8];
129
+ }, $[9] = t2) : t2 = $[9];
34
130
  let t3;
35
- $[9] === icon.node ? t3 = $[10] : (t3 = /* @__PURE__ */ jsx(Drawing, {
36
- node: icon.node,
131
+ $[10] === icon.drawing ? t3 = $[11] : (t3 = /* @__PURE__ */ jsx(Drawing, {
132
+ drawing: icon.drawing,
37
133
  width: "1.25em",
38
- height: "1.25em",
39
- strokeWidth: 1.5
40
- }), $[9] = icon.node, $[10] = t3);
134
+ height: "1.25em"
135
+ }), $[10] = icon.drawing, $[11] = t3);
41
136
  let t4;
42
- $[11] !== icon || $[12] !== onSelect ? (t4 = () => {
137
+ $[12] !== icon || $[13] !== onSelect ? (t4 = () => {
43
138
  onSelect(icon);
44
- }, $[11] = icon, $[12] = onSelect, $[13] = t4) : t4 = $[13];
139
+ }, $[12] = icon, $[13] = onSelect, $[14] = t4) : t4 = $[14];
45
140
  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, {
141
+ $[15] !== icon || $[16] !== onHover ? (t5 = (event) => {
142
+ onHover(icon, event.currentTarget);
143
+ }, $[15] = icon, $[16] = onHover, $[17] = t5) : t5 = $[17];
144
+ let t6;
145
+ return $[18] !== icon.label || $[19] !== position || $[20] !== props || $[21] !== selected || $[22] !== t1 || $[23] !== t3 || $[24] !== t4 || $[25] !== t5 || $[26] !== total ? (t6 = /* @__PURE__ */ jsx(Button, {
47
146
  type: "button",
48
147
  role: "option",
49
148
  "aria-selected": selected,
@@ -56,83 +155,135 @@ function Cell(t0) {
56
155
  style: t2,
57
156
  icon: t3,
58
157
  "aria-label": icon.label,
59
- "data-icon": icon.name,
60
158
  onClick: t4,
159
+ onMouseEnter: t5,
61
160
  ...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;
161
+ }), $[18] = icon.label, $[19] = position, $[20] = props, $[21] = selected, $[22] = t1, $[23] = t3, $[24] = t4, $[25] = t5, $[26] = total, $[27] = t6) : t6 = $[27], t6;
63
162
  }
64
163
  function Library(t0) {
65
- let $ = c(27), activeCell, activeIndex, icons, onClearSearch, onSelect, props, rows, search, selected;
66
- $[0] === t0 ? (activeCell = $[1], activeIndex = $[2], icons = $[3], onClearSearch = $[4], onSelect = $[5], props = $[6], rows = $[7], search = $[8], selected = $[9]) : ({icons, rows, selected, activeIndex, activeCell, search, onSelect, onClearSearch, ...props} = t0, $[0] = t0, $[1] = activeCell, $[2] = activeIndex, $[3] = icons, $[4] = onClearSearch, $[5] = onSelect, $[6] = props, $[7] = rows, $[8] = search, $[9] = selected);
67
- let { totalRows, firstRow, lastRow } = rows, t1;
68
- if ($[10] !== activeCell || $[11] !== activeIndex || $[12] !== firstRow || $[13] !== icons || $[14] !== lastRow || $[15] !== onClearSearch || $[16] !== onSelect || $[17] !== props || $[18] !== search || $[19] !== selected || $[20] !== totalRows) {
69
- let visible = icons.slice(firstRow * 8, lastRow * 8), t2;
70
- $[22] === Symbol.for("react.memo_cache_sentinel") ? (t2 = {
164
+ let $ = c(44), activeCell, activeIndex, icons, onClearSearch, onHover, onSelect, props, rows, search, selected;
165
+ $[0] === t0 ? (activeCell = $[1], activeIndex = $[2], icons = $[3], onClearSearch = $[4], onHover = $[5], onSelect = $[6], props = $[7], rows = $[8], search = $[9], selected = $[10]) : ({icons, rows, selected, activeIndex, activeCell, search, onSelect, onHover, onClearSearch, ...props} = t0, $[0] = t0, $[1] = activeCell, $[2] = activeIndex, $[3] = icons, $[4] = onClearSearch, $[5] = onHover, $[6] = onSelect, $[7] = props, $[8] = rows, $[9] = search, $[10] = selected);
166
+ let { totalRows, firstRow, lastRow } = rows, T0, t1, t2, t3, t4, t5, t6;
167
+ if ($[11] !== activeCell || $[12] !== activeIndex || $[13] !== firstRow || $[14] !== icons || $[15] !== lastRow || $[16] !== onClearSearch || $[17] !== onHover || $[18] !== onSelect || $[19] !== props || $[20] !== search || $[21] !== selected || $[22] !== totalRows) {
168
+ let visible = icons.slice(firstRow * 8, lastRow * 8);
169
+ T0 = Box, t1 = "listbox", t2 = "Icon library", $[30] === icons ? t3 = $[31] : (t3 = isDefined(icons) ? {
71
170
  height: 348,
72
171
  overflowY: "auto"
73
- }, $[22] = t2) : t2 = $[22];
74
- let t3;
75
- $[23] !== icons.length || $[24] !== onClearSearch || $[25] !== search ? (t3 = icons.length === 0 && /* @__PURE__ */ jsxs(Flex, {
172
+ } : void 0, $[30] = icons, $[31] = t3), t4 = props, $[32] !== icons || $[33] !== onClearSearch || $[34] !== search ? (t5 = !isDefined(icons) && /* @__PURE__ */ jsxs(Flex, {
76
173
  align: "center",
77
174
  justify: "center",
78
175
  direction: "column",
79
176
  gap: 3,
80
- style: { height: 348 },
81
- children: [/* @__PURE__ */ jsxs(Text, {
177
+ paddingTop: 5,
178
+ paddingBottom: 6,
179
+ children: [/* @__PURE__ */ jsx(Text, {
82
180
  size: 1,
83
181
  muted: !0,
84
- children: [
85
- "Nothing matches “",
86
- search,
87
- "”."
88
- ]
182
+ children: `Nothing matches "${search}".`
89
183
  }), /* @__PURE__ */ jsx(Button, {
90
184
  type: "button",
91
185
  mode: "ghost",
92
186
  text: "Clear search",
93
187
  onClick: onClearSearch
94
188
  })]
95
- }), $[23] = icons.length, $[24] = onClearSearch, $[25] = search, $[26] = t3) : t3 = $[26], t1 = /* @__PURE__ */ jsxs(Box, {
96
- role: "listbox",
97
- "aria-label": "Icon library",
98
- style: t2,
99
- ...props,
100
- children: [t3, icons.length > 0 && /* @__PURE__ */ jsx(Grid, {
101
- gridTemplateColumns: 8,
102
- style: {
103
- ...gridLayoutStyle,
104
- paddingTop: firstRow * 44,
105
- paddingBottom: (totalRows - lastRow) * 44
106
- },
107
- children: visible.map((icon, offset) => {
108
- let index = firstRow * 8 + offset;
109
- return /* @__PURE__ */ jsx(Cell, {
110
- ref: index === activeIndex ? activeCell : void 0,
111
- icon,
112
- selected: icon.name === selected,
113
- focusable: index === activeIndex,
114
- position: index + 1,
115
- total: icons.length,
116
- onSelect
117
- }, icon.name);
118
- })
119
- })]
120
- }), $[10] = activeCell, $[11] = activeIndex, $[12] = firstRow, $[13] = icons, $[14] = lastRow, $[15] = onClearSearch, $[16] = onSelect, $[17] = props, $[18] = search, $[19] = selected, $[20] = totalRows, $[21] = t1;
121
- } else t1 = $[21];
122
- return t1;
189
+ }), $[32] = icons, $[33] = onClearSearch, $[34] = search, $[35] = t5) : t5 = $[35], t6 = isDefined(icons) && /* @__PURE__ */ jsx(Grid, {
190
+ gridTemplateColumns: 8,
191
+ style: {
192
+ ...gridLayoutStyle,
193
+ paddingTop: firstRow * 44,
194
+ paddingBottom: (totalRows - lastRow) * 44
195
+ },
196
+ children: visible.map((icon, offset) => {
197
+ let index = firstRow * 8 + offset;
198
+ return /* @__PURE__ */ jsx(Cell, {
199
+ ref: index === activeIndex ? activeCell : void 0,
200
+ icon,
201
+ selected: icon.name === selected,
202
+ focusable: index === activeIndex,
203
+ position: index + 1,
204
+ total: icons.length,
205
+ onSelect,
206
+ onHover
207
+ }, icon.name);
208
+ })
209
+ }), $[11] = activeCell, $[12] = activeIndex, $[13] = firstRow, $[14] = icons, $[15] = lastRow, $[16] = onClearSearch, $[17] = onHover, $[18] = onSelect, $[19] = props, $[20] = search, $[21] = selected, $[22] = totalRows, $[23] = T0, $[24] = t1, $[25] = t2, $[26] = t3, $[27] = t4, $[28] = t5, $[29] = t6;
210
+ } else T0 = $[23], t1 = $[24], t2 = $[25], t3 = $[26], t4 = $[27], t5 = $[28], t6 = $[29];
211
+ let t7;
212
+ return $[36] !== T0 || $[37] !== t1 || $[38] !== t2 || $[39] !== t3 || $[40] !== t4 || $[41] !== t5 || $[42] !== t6 ? (t7 = /* @__PURE__ */ jsxs(T0, {
213
+ role: t1,
214
+ "aria-label": t2,
215
+ style: t3,
216
+ ...t4,
217
+ children: [t5, t6]
218
+ }), $[36] = T0, $[37] = t1, $[38] = t2, $[39] = t3, $[40] = t4, $[41] = t5, $[42] = t6, $[43] = t7) : t7 = $[43], t7;
219
+ }
220
+ function CodeText(t0) {
221
+ let $ = c(10), props, text;
222
+ $[0] === t0 ? (props = $[1], text = $[2]) : ({text, ...props} = t0, $[0] = t0, $[1] = props, $[2] = text);
223
+ let t1, t2;
224
+ if ($[3] !== props || $[4] !== text) {
225
+ let parts = text.split("`");
226
+ t1 = props, t2 = parts.map((part, index) => {
227
+ let start = parts.slice(0, index).join("`").length;
228
+ return index % 2 == 1 ? /* @__PURE__ */ jsx("code", { children: part }, start) : part;
229
+ }), $[3] = props, $[4] = text, $[5] = t1, $[6] = t2;
230
+ } else t1 = $[5], t2 = $[6];
231
+ let t3;
232
+ return $[7] !== t1 || $[8] !== t2 ? (t3 = /* @__PURE__ */ jsx("span", {
233
+ ...t1,
234
+ children: t2
235
+ }), $[7] = t1, $[8] = t2, $[9] = t3) : t3 = $[9], t3;
236
+ }
237
+ function Problem(t0) {
238
+ let $ = c(14), problem, props;
239
+ $[0] === t0 ? (problem = $[1], props = $[2]) : ({problem, ...props} = t0, $[0] = t0, $[1] = problem, $[2] = props);
240
+ let t1;
241
+ $[3] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(Text, {
242
+ size: 1,
243
+ children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
244
+ }), $[3] = t1) : t1 = $[3];
245
+ let t2;
246
+ $[4] === problem.title ? t2 = $[5] : (t2 = /* @__PURE__ */ jsx(Text, {
247
+ size: 1,
248
+ weight: "medium",
249
+ children: /* @__PURE__ */ jsx(CodeText, { text: problem.title })
250
+ }), $[4] = problem.title, $[5] = t2);
251
+ let t3;
252
+ $[6] === problem.description ? t3 = $[7] : (t3 = /* @__PURE__ */ jsx(Text, {
253
+ size: 1,
254
+ muted: !0,
255
+ children: /* @__PURE__ */ jsx(CodeText, { text: problem.description })
256
+ }), $[6] = problem.description, $[7] = t3);
257
+ let t4;
258
+ $[8] !== t2 || $[9] !== t3 ? (t4 = /* @__PURE__ */ jsxs(Flex, {
259
+ gap: 3,
260
+ children: [t1, /* @__PURE__ */ jsxs(Stack, {
261
+ gap: 3,
262
+ children: [t2, t3]
263
+ })]
264
+ }), $[8] = t2, $[9] = t3, $[10] = t4) : t4 = $[10];
265
+ let t5;
266
+ return $[11] !== props || $[12] !== t4 ? (t5 = /* @__PURE__ */ jsx(Card, {
267
+ tone: "caution",
268
+ padding: 4,
269
+ radius: 2,
270
+ border: !0,
271
+ ...props,
272
+ children: t4
273
+ }), $[11] = props, $[12] = t4, $[13] = t5) : t5 = $[13], t5;
123
274
  }
124
275
  function Recent(t0) {
125
- let $ = c(23), icons, onForget, onSelect, props, selected;
126
- $[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);
276
+ let $ = c(26), icons, onForget, onHover, onSelect, props, selected;
277
+ $[0] === t0 ? (icons = $[1], onForget = $[2], onHover = $[3], onSelect = $[4], props = $[5], selected = $[6]) : ({icons, selected, onSelect, onHover, onForget, ...props} = t0, $[0] = t0, $[1] = icons, $[2] = onForget, $[3] = onHover, $[4] = onSelect, $[5] = props, $[6] = selected);
127
278
  let t1;
128
- $[6] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(Text, {
279
+ $[7] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(Text, {
129
280
  size: 1,
130
281
  weight: "medium",
131
282
  muted: !0,
132
283
  children: "Recent icons"
133
- }), $[6] = t1) : t1 = $[6];
284
+ }), $[7] = t1) : t1 = $[7];
134
285
  let t2;
135
- $[7] === onForget ? t2 = $[8] : (t2 = /* @__PURE__ */ jsxs(Flex, {
286
+ $[8] === onForget ? t2 = $[9] : (t2 = /* @__PURE__ */ jsxs(Flex, {
136
287
  align: "center",
137
288
  justify: "space-between",
138
289
  gap: 2,
@@ -143,98 +294,74 @@ function Recent(t0) {
143
294
  padding: 2,
144
295
  text: "Clear",
145
296
  onClick: onForget,
146
- "aria-label": "Forget the recently used icons."
297
+ "aria-label": "Clear recent icons"
147
298
  })]
148
- }), $[7] = onForget, $[8] = t2);
299
+ }), $[8] = onForget, $[9] = t2);
149
300
  let t3;
150
- if ($[9] !== icons || $[10] !== onSelect || $[11] !== selected) {
301
+ if ($[10] !== icons || $[11] !== onHover || $[12] !== onSelect || $[13] !== selected) {
151
302
  let t4;
152
- $[13] !== icons.length || $[14] !== onSelect || $[15] !== selected ? (t4 = (icon, index) => /* @__PURE__ */ jsx(Cell, {
303
+ $[15] !== icons.length || $[16] !== onHover || $[17] !== onSelect || $[18] !== selected ? (t4 = (icon, index) => /* @__PURE__ */ jsx(Cell, {
153
304
  icon,
154
305
  selected: icon.name === selected,
155
306
  focusable: !0,
156
307
  position: index + 1,
157
308
  total: icons.length,
158
- onSelect
159
- }, 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;
160
- } else t3 = $[12];
309
+ onSelect,
310
+ onHover
311
+ }, icon.name), $[15] = icons.length, $[16] = onHover, $[17] = onSelect, $[18] = selected, $[19] = t4) : t4 = $[19], t3 = icons.map(t4), $[10] = icons, $[11] = onHover, $[12] = onSelect, $[13] = selected, $[14] = t3;
312
+ } else t3 = $[14];
161
313
  let t4;
162
- $[17] === t3 ? t4 = $[18] : (t4 = /* @__PURE__ */ jsx(Grid, {
314
+ $[20] === t3 ? t4 = $[21] : (t4 = /* @__PURE__ */ jsx(Grid, {
163
315
  role: "listbox",
164
316
  "aria-label": "Recent icons",
165
317
  gridTemplateColumns: 8,
166
318
  style: gridLayoutStyle,
167
319
  children: t3
168
- }), $[17] = t3, $[18] = t4);
320
+ }), $[20] = t3, $[21] = t4);
169
321
  let t5;
170
- return $[19] !== props || $[20] !== t2 || $[21] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
322
+ return $[22] !== props || $[23] !== t2 || $[24] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
171
323
  gap: 3,
172
324
  ...props,
173
325
  children: [t2, t4]
174
- }), $[19] = props, $[20] = t2, $[21] = t4, $[22] = t5) : t5 = $[22], t5;
175
- }
176
- function Tooltip(t0) {
177
- let $ = c(14), hovered, props;
178
- $[0] === t0 ? (hovered = $[1], props = $[2]) : ({hovered, ...props} = t0, $[0] = t0, $[1] = hovered, $[2] = props);
179
- let t1;
180
- $[3] === hovered ? t1 = $[4] : (t1 = isDefined(hovered), $[3] = hovered, $[4] = t1);
181
- let t2 = hovered?.element ?? null, t3, t4;
182
- $[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = [
183
- -4,
184
- 0,
185
- 0,
186
- 0
187
- ], t4 = { pointerEvents: "none" }, $[5] = t3, $[6] = t4) : (t3 = $[5], t4 = $[6]);
188
- let t5;
189
- $[7] === hovered ? t5 = $[8] : (t5 = isDefined(hovered) && /* @__PURE__ */ jsxs(Stack, {
190
- gap: 3,
191
- style: { maxWidth: "16rem" },
192
- children: [/* @__PURE__ */ jsx(Text, {
193
- size: 1,
194
- weight: "medium",
195
- children: hovered.icon.label
196
- }), isDefined(hovered.icon.tags) && /* @__PURE__ */ jsx(Text, {
197
- size: 1,
198
- muted: !0,
199
- children: hovered.icon.tags
200
- })]
201
- }), $[7] = hovered, $[8] = t5);
202
- let t6;
203
- return $[9] !== props || $[10] !== t1 || $[11] !== t2 || $[12] !== t5 ? (t6 = /* @__PURE__ */ jsx(Popover, {
204
- open: t1,
205
- referenceElement: t2,
206
- placement: "top",
207
- portal: !0,
208
- padding: 3,
209
- __unstable_margins: t3,
210
- style: t4,
211
- content: t5,
212
- ...props
213
- }), $[9] = props, $[10] = t1, $[11] = t2, $[12] = t5, $[13] = t6) : t6 = $[13], t6;
326
+ }), $[22] = props, $[23] = t2, $[24] = t4, $[25] = t5) : t5 = $[25], t5;
214
327
  }
215
- /** Properties every icon the Studio draws is rendered with. */
216
- const defaultIconProps = {
217
- width: "1em",
218
- height: "1em",
219
- strokeWidth: 1.5
220
- };
221
328
  /**
222
- * Flattens text into the form a search compares against, so a hyphenated icon name and a multi-word
223
- * tag are matched the same way.
329
+ * Normalises text for search, so hyphenated names and multi-word keywords match the same way.
224
330
  *
225
- * @param value - The text to flatten.
226
- * @returns The text in comparable form.
331
+ * @param value - The text.
332
+ * @returns The normalised text.
227
333
  */
228
- function normaliseTerms(value) {
334
+ function normalizeTerms(value) {
229
335
  return value.toLowerCase().replaceAll(/[\s-]+/gu, " ").trim();
230
336
  }
231
337
  /**
232
- * Narrows the library to a chosen set of icons, in the order they were named. A name the library does
233
- * not recognise is skipped, so a set outlives the release it was written against.
338
+ * Splits text into the parts a search matches and the parts it doesn't, matching the way search does: ignoring case,
339
+ * and treating spaces and hyphens alike.
340
+ *
341
+ * @param text - The text to split.
342
+ * @param query - The normalised search.
343
+ * @returns The parts in order, each with where it starts in the text and whether it matches.
344
+ */
345
+ function splitMatches(text, query) {
346
+ if (!isDefined(query)) return [{
347
+ text,
348
+ start: 0,
349
+ match: !1
350
+ }];
351
+ let pattern = query.split(" ").map((word) => word.replaceAll(/[$()*+.?[\\\]^{|}]/gu, String.raw`\$&`)).join(String.raw`[\s-]+`), parts = text.split(RegExp(`(${pattern})`, "giu"));
352
+ return parts.map((part, index) => ({
353
+ text: part,
354
+ start: parts.slice(0, index).join("").length,
355
+ match: index % 2 == 1
356
+ }));
357
+ }
358
+ /**
359
+ * Narrows the library to the named icons, in the order given. Unknown names are skipped, so a list keeps working
360
+ * across library releases.
234
361
  *
235
- * @param library - Every icon the library offers.
236
- * @param names - Names of the icons to offer, or nothing to offer them all.
237
- * @returns The icons on offer.
362
+ * @param library - Every icon in the library.
363
+ * @param names - The icons to offer, or undefined for all of them.
364
+ * @returns The icons to offer.
238
365
  */
239
366
  function selectIcons(library, names) {
240
367
  if (!isDefined(names)) return library;
@@ -245,165 +372,320 @@ function selectIcons(library, names) {
245
372
  });
246
373
  }
247
374
  /**
248
- * Reads the icon library, pairing each drawing with the terms it can be found by. Both files come
249
- * from the same release as the icons themselves, so the drawings and their terms never disagree.
375
+ * Lists identifiers as inline code, separated by commas.
250
376
  *
251
- * @returns Every icon the library offers.
377
+ * @param identifiers - The identifiers.
378
+ * @returns The list.
252
379
  */
253
- async function readLibrary() {
254
- let [drawings, tags] = await Promise.all([import("lucide-static/icon-nodes.json"), import("lucide-static/tags.json")]), nodes = drawings.default, terms = tags.default;
255
- return Object.keys(nodes).flatMap((name) => {
256
- let node = parseIconNode(nodes[name]);
257
- if (!isDefined(node)) return [];
258
- let iconTags = terms[name] ?? [];
259
- return [{
260
- name,
261
- label: convertCase(name, "sentence"),
262
- tags: iconTags.join(", "),
263
- node,
264
- terms: normaliseTerms([name, ...iconTags].join(" "))
265
- }];
266
- });
380
+ function formatList(identifiers) {
381
+ return identifiers.map((identifier) => `\`${identifier}\``).join(", ");
382
+ }
383
+ /**
384
+ * Describes why a library the project hasn't installed can't be shown, and what to add.
385
+ *
386
+ * @param label - Readable name of the library.
387
+ * @param packageName - Package the library is read from, or an example of one for a family of packages.
388
+ * @param family - Whether the library is a family of packages, one per style.
389
+ * @param listed - Whether the project's `package.json` lists the package.
390
+ * @param sitePackage - Another package of the library the project lists, or undefined when there's none.
391
+ * @returns The problem.
392
+ */
393
+ function describeMissingLibrary(label, packageName, family, listed, sitePackage) {
394
+ return listed ? {
395
+ title: `${label} isn't installed yet`,
396
+ description: `\`${packageName}\` is in \`package.json\` but not in \`node_modules\`. Reinstall the project's dependencies.`
397
+ } : isDefined(sitePackage) ? {
398
+ title: `${label} needs \`${packageName}\``,
399
+ description: `The Studio reads ${label} from \`${packageName}\`, not \`${sitePackage}\`. Add it as a dev dependency.`
400
+ } : {
401
+ title: `${label} isn't installed`,
402
+ description: family ? `Install a ${label} style package, such as \`${packageName}\`.` : `Install \`${packageName}\` in the project.`
403
+ };
404
+ }
405
+ /**
406
+ * Loads one library style from the sets the build step converted.
407
+ *
408
+ * @param library - Identifier of the library, or undefined when none is set.
409
+ * @param style - Identifier of the style, or undefined for the library's default.
410
+ * @returns The icons, or why they can't be shown.
411
+ */
412
+ async function readLibrary(library, style) {
413
+ if (!isDefined(library)) return { problem: {
414
+ title: "No icon library set",
415
+ description: "Set `library` in the icon plugin's configuration or in the field's options."
416
+ } };
417
+ if (!iconRegistry.ready) return { problem: {
418
+ title: "Icons aren't set up",
419
+ description: "Add `withIcons()` to the Studio's build configuration, then restart the Studio."
420
+ } };
421
+ let info = iconRegistry.libraries[library];
422
+ if (!isDefined(info)) return { problem: {
423
+ title: "Unknown icon library",
424
+ description: `There's no icon library called \`${library}\`. Use one of: ${formatList(Object.keys(iconRegistry.libraries))}.`
425
+ } };
426
+ let { label, packageName, packagePrefix, styles, listed, installed, sitePackage } = info;
427
+ if (!installed) return { problem: describeMissingLibrary(label, packageName, isDefined(packagePrefix), listed, sitePackage) };
428
+ let resolvedStyle = style ?? styles[0] ?? "", loader = iconRegistry.sets[getIconSetKey(library, resolvedStyle)];
429
+ if (!isDefined(loader)) return { problem: isDefined(packagePrefix) ? {
430
+ title: `${label} style \`${resolvedStyle}\` isn't installed`,
431
+ description: `Install \`${packagePrefix}${resolvedStyle}\` in the project.`
432
+ } : {
433
+ title: `Unknown ${label} style`,
434
+ description: `${label} has no \`${resolvedStyle}\` style. Use one of: ${formatList(styles)}.`
435
+ } };
436
+ let module = await loader(), set = parseIconSet(module.default);
437
+ return isDefined(set) ? { icons: set.icons.map(({ name, keywords, node, root }) => ({
438
+ name,
439
+ label: convertCase(name, "title"),
440
+ tags: keywords.join(", "),
441
+ drawing: {
442
+ root: root ?? set.root,
443
+ node
444
+ },
445
+ terms: normalizeTerms([name, ...keywords].join(" "))
446
+ })) } : { problem: {
447
+ title: `Couldn't load ${label}`,
448
+ description: "Restart the Studio and try again."
449
+ } };
267
450
  }
268
- /** The library once asked for, held so every field in a Studio session shares the one request. */
269
- let libraryRequest;
451
+ /** Library requests shared by every field in the session, keyed by library and style, since the data never changes. */
452
+ const libraryRequests = /* @__PURE__ */ new Map();
270
453
  /**
271
- * Reads the icon library, sharing a single request across every field opened in the session. The
272
- * library is a megabyte of data that never changes within a release, so asking twice only ever costs.
454
+ * Loads a library style, reusing a request already in progress.
273
455
  *
274
- * @returns Every icon the library offers.
456
+ * @param library - Identifier of the library, or undefined when none is set.
457
+ * @param style - Identifier of the style, or undefined for the library's default.
458
+ * @returns The icons, or why they can't be shown.
459
+ */
460
+ async function requestLibrary(library, style) {
461
+ let key = getIconSetKey(library ?? "", style ?? ""), request = libraryRequests.get(key);
462
+ return isDefined(request) || (request = readLibrary(library, style), libraryRequests.set(key, request)), await request;
463
+ }
464
+ /**
465
+ * Gap between the tooltip and its cell, set through margins because `@sanity/ui` offers no other way.
466
+ * Only the top is set, since the tooltip always appears above the cell.
275
467
  */
276
- async function requestLibrary() {
277
- return libraryRequest ??= readLibrary(), await libraryRequest;
468
+ const tooltipMargins = [
469
+ -4,
470
+ 0,
471
+ 0,
472
+ 0
473
+ ], matchStyle = {
474
+ background: "none",
475
+ color: "inherit",
476
+ textDecorationLine: "underline",
477
+ textUnderlineOffset: "0.2em"
478
+ };
479
+ function Highlight(t0) {
480
+ let $ = c(10), props, query, text;
481
+ $[0] === t0 ? (props = $[1], query = $[2], text = $[3]) : ({text, query, ...props} = t0, $[0] = t0, $[1] = props, $[2] = query, $[3] = text);
482
+ let t1;
483
+ $[4] !== query || $[5] !== text ? (t1 = splitMatches(text, query).map(_temp$1), $[4] = query, $[5] = text, $[6] = t1) : t1 = $[6];
484
+ let t2;
485
+ return $[7] !== props || $[8] !== t1 ? (t2 = /* @__PURE__ */ jsx("span", {
486
+ ...props,
487
+ children: t1
488
+ }), $[7] = props, $[8] = t1, $[9] = t2) : t2 = $[9], t2;
489
+ }
490
+ function _temp$1(part) {
491
+ return part.match ? /* @__PURE__ */ jsx("mark", {
492
+ style: matchStyle,
493
+ children: part.text
494
+ }, part.start) : part.text;
495
+ }
496
+ function Tooltip(t0) {
497
+ let $ = c(15), hovered, props, query;
498
+ $[0] === t0 ? (hovered = $[1], props = $[2], query = $[3]) : ({hovered, query, ...props} = t0, $[0] = t0, $[1] = hovered, $[2] = props, $[3] = query);
499
+ let t1;
500
+ $[4] === hovered ? t1 = $[5] : (t1 = isDefined(hovered), $[4] = hovered, $[5] = t1);
501
+ let t2 = hovered?.element ?? null, t3;
502
+ $[6] === Symbol.for("react.memo_cache_sentinel") ? (t3 = { pointerEvents: "none" }, $[6] = t3) : t3 = $[6];
503
+ let t4;
504
+ $[7] !== hovered || $[8] !== query ? (t4 = isDefined(hovered) && /* @__PURE__ */ jsxs(Stack, {
505
+ gap: 3,
506
+ style: { maxWidth: "16rem" },
507
+ children: [/* @__PURE__ */ jsx(Text, {
508
+ size: 1,
509
+ weight: "medium",
510
+ children: /* @__PURE__ */ jsx(Highlight, {
511
+ text: hovered.icon.label,
512
+ query
513
+ })
514
+ }), isDefined(hovered.icon.tags) && /* @__PURE__ */ jsx(Text, {
515
+ size: 1,
516
+ muted: !0,
517
+ children: /* @__PURE__ */ jsx(Highlight, {
518
+ text: hovered.icon.tags,
519
+ query
520
+ })
521
+ })]
522
+ }), $[7] = hovered, $[8] = query, $[9] = t4) : t4 = $[9];
523
+ let t5;
524
+ return $[10] !== props || $[11] !== t1 || $[12] !== t2 || $[13] !== t4 ? (t5 = /* @__PURE__ */ jsx(Popover, {
525
+ open: t1,
526
+ referenceElement: t2,
527
+ placement: "top",
528
+ portal: !0,
529
+ padding: 3,
530
+ __unstable_margins: tooltipMargins,
531
+ style: t3,
532
+ content: t4,
533
+ ...props
534
+ }), $[10] = props, $[11] = t1, $[12] = t2, $[13] = t4, $[14] = t5) : t5 = $[14], t5;
278
535
  }
279
536
  /**
280
- * Reads the icon library, sharing one request with every other field in the session. Callers mount
281
- * only while a picker is open, so a Studio that never opens one never pays the megabyte it costs.
537
+ * Loads an icon library style. Only an open picker calls this, so a Studio that never opens one never loads it.
282
538
  *
283
- * @returns Every icon the library offers, or undefined while it is still being read.
539
+ * @param library - Identifier of the library, or undefined when none is set.
540
+ * @param style - Identifier of the style, or undefined for the library's default.
541
+ * @returns The icons or why they can't be shown, or undefined while loading.
284
542
  */
285
- function useIconLibrary() {
286
- let $ = c(2), [library, setLibrary] = useState(), t0, t1;
287
- return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
543
+ function useIconLibrary(library, style) {
544
+ let $ = c(4), [result, setResult] = useState(), t0, t1;
545
+ return $[0] !== library || $[1] !== style ? (t0 = () => {
288
546
  let active = !0;
289
547
  return (async function load() {
290
- let loaded = await requestLibrary();
291
- active && setLibrary(loaded);
548
+ let loaded = await requestLibrary(library, style);
549
+ active && setResult(loaded);
292
550
  })(), () => {
293
551
  active = !1;
294
552
  };
295
- }, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1), library;
553
+ }, t1 = [library, style], $[0] = library, $[1] = style, $[2] = t0, $[3] = t1) : (t0 = $[2], t1 = $[3]), useEffect(t0, t1), result;
296
554
  }
297
555
  /**
298
- * Tracks which icon the cursor rests on, holding a tooltip back until it has settled somewhere.
556
+ * Tracks the icon under the cursor, showing its tooltip after a short delay. Tooltips are held back while the grid
557
+ * scrolls, and the icon left under the cursor gets its tooltip once scrolling stops.
299
558
  *
300
- * @param byName - Every icon the library offers, keyed by the name it goes by.
301
- * @param scrolling - Whether the grid is currently moving under the cursor.
302
- * @returns The icon being hovered, alongside ways to follow the cursor and to dismiss it.
559
+ * @param scrolling - Whether the grid is scrolling.
560
+ * @returns The hovered icon, and functions to update and clear it.
303
561
  */
304
- function useIconTooltip(byName, scrolling) {
305
- let $ = c(12), [hovered, setHovered] = useState(), hoverStart = useRef(void 0), t0, t1;
562
+ function useIconTooltip(scrolling) {
563
+ let $ = c(14), [hovered, setHovered] = useState(), hoverStart = useRef(void 0), scrolledOnto = useRef(void 0), t0, t1;
306
564
  $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => () => {
307
565
  isDefined(hoverStart.current) && clearTimeout(hoverStart.current);
308
566
  }, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
309
- let t2;
310
- $[2] === hovered ? t2 = $[3] : (t2 = function clearHover() {
567
+ let t2, t3;
568
+ $[2] === scrolling ? (t2 = $[3], t3 = $[4]) : (t2 = () => {
569
+ let landed = scrolledOnto.current;
570
+ !scrolling && isDefined(landed) && (scrolledOnto.current = void 0, landed.element.isConnected && landed.element.matches(":hover") && (hoverStart.current = setTimeout(() => {
571
+ setHovered(landed);
572
+ }, 200)));
573
+ }, t3 = [scrolling], $[2] = scrolling, $[3] = t2, $[4] = t3), useEffect(t2, t3);
574
+ let t4;
575
+ $[5] === hovered ? t4 = $[6] : (t4 = function clearHover() {
311
576
  isDefined(hoverStart.current) && clearTimeout(hoverStart.current), isDefined(hovered) && setHovered(void 0);
312
- }, $[2] = hovered, $[3] = t2);
313
- let clearHover = t2, t3;
314
- $[4] !== byName || $[5] !== hovered || $[6] !== scrolling ? (t3 = function handleHover(event) {
315
- if (scrolling) return;
316
- let target = event.target instanceof Element ? event.target.closest("[data-icon]") : void 0, cell = target instanceof HTMLElement ? target : void 0;
317
- if (cell === hovered?.element) return;
318
- let name = cell?.dataset.icon, icon = isDefined(name) ? byName.get(name) : void 0;
319
- if (isDefined(hoverStart.current) && clearTimeout(hoverStart.current), !isDefined(icon) || !isDefined(cell)) {
320
- setHovered(void 0);
321
- return;
322
- }
323
- if (isDefined(hovered)) {
324
- setHovered({
577
+ }, $[5] = hovered, $[6] = t4);
578
+ let clearHover = t4, t5;
579
+ $[7] !== hovered || $[8] !== scrolling ? (t5 = function handleHover(icon, element) {
580
+ if (scrolling) {
581
+ scrolledOnto.current = {
325
582
  icon,
326
- element: cell
327
- });
583
+ element
584
+ };
328
585
  return;
329
586
  }
330
- hoverStart.current = setTimeout(() => {
331
- setHovered({
332
- icon,
333
- element: cell
334
- });
335
- }, 200);
336
- }, $[4] = byName, $[5] = hovered, $[6] = scrolling, $[7] = t3) : t3 = $[7];
337
- let handleHover = t3, t4;
338
- return $[8] !== clearHover || $[9] !== handleHover || $[10] !== hovered ? (t4 = {
587
+ if (element !== hovered?.element) {
588
+ if (isDefined(hoverStart.current) && clearTimeout(hoverStart.current), isDefined(hovered)) {
589
+ setHovered({
590
+ icon,
591
+ element
592
+ });
593
+ return;
594
+ }
595
+ hoverStart.current = setTimeout(() => {
596
+ setHovered({
597
+ icon,
598
+ element
599
+ });
600
+ }, 200);
601
+ }
602
+ }, $[7] = hovered, $[8] = scrolling, $[9] = t5) : t5 = $[9];
603
+ let handleHover = t5, t6;
604
+ return $[10] !== clearHover || $[11] !== handleHover || $[12] !== hovered ? (t6 = {
339
605
  hovered,
340
606
  handleHover,
341
607
  clearHover
342
- }, $[8] = clearHover, $[9] = handleHover, $[10] = hovered, $[11] = t4) : t4 = $[11], t4;
608
+ }, $[10] = clearHover, $[11] = handleHover, $[12] = hovered, $[13] = t6) : t6 = $[13], t6;
609
+ }
610
+ /**
611
+ * Builds the storage key for one library style.
612
+ *
613
+ * @param scope - The library and style, such as `phosphor/bold`.
614
+ * @returns The storage key.
615
+ */
616
+ function getStorageKey(scope) {
617
+ return `driftime:sanity-plugin-icon:recent:${scope}`;
343
618
  }
344
- /** Where the icons an author reached for last are kept, in the browser rather than the dataset. */
345
- const storageKey = "driftime:sanity-plugin-icon:recent";
346
619
  /**
347
- * Reads the icons an author reached for last.
620
+ * Reads the recently chosen icons.
348
621
  *
349
- * @returns Names of the remembered icons, most recent first.
622
+ * @param scope - The library and style, such as `phosphor/bold`.
623
+ * @returns The icon names, most recent first.
350
624
  */
351
- function readRecent() {
625
+ function readRecent(scope) {
352
626
  try {
353
- let stored = JSON.parse(globalThis.localStorage.getItem(storageKey) ?? "[]");
627
+ let stored = JSON.parse(globalThis.localStorage.getItem(getStorageKey(scope)) ?? "[]");
354
628
  return Array.isArray(stored) ? stored.filter((entry) => typeof entry == "string") : [];
355
629
  } catch {
356
630
  return [];
357
631
  }
358
632
  }
359
633
  /**
360
- * Remembers an icon as the one reached for most recently.
634
+ * Adds an icon to the front of the recent list.
361
635
  *
362
- * @param name - Name of the chosen icon.
363
- * @returns Names of the remembered icons, most recent first.
636
+ * @param scope - The library and style, such as `phosphor/bold`.
637
+ * @param name - The chosen icon's name.
638
+ * @returns The updated icon names, most recent first.
364
639
  */
365
- function writeRecent(name) {
366
- let updated = [name, ...readRecent().filter((entry) => entry !== name)].slice(0, 8);
640
+ function writeRecent(scope, name) {
641
+ let updated = [name, ...readRecent(scope).filter((entry) => entry !== name)].slice(0, 8);
367
642
  try {
368
- globalThis.localStorage.setItem(storageKey, JSON.stringify(updated));
643
+ globalThis.localStorage.setItem(getStorageKey(scope), JSON.stringify(updated));
369
644
  } catch {}
370
645
  return updated;
371
646
  }
372
- /** Forgets every icon reached for so far. */
373
- function clearRecent() {
647
+ /**
648
+ * Clears the recent icons.
649
+ *
650
+ * @param scope - The library and style, such as `phosphor/bold`.
651
+ */
652
+ function clearRecent(scope) {
374
653
  try {
375
- globalThis.localStorage.removeItem(storageKey);
654
+ globalThis.localStorage.removeItem(getStorageKey(scope));
376
655
  } catch {}
377
656
  }
378
657
  /**
379
- * Tracks the icons an author reached for last, keeping what is remembered and what is drawn in step.
380
- * The stored list is read as the picker mounts, so another tab choosing an icon is picked up too.
658
+ * Tracks the icons an author chose most recently from one library style. The list is read when the picker opens, so
659
+ * choices made in another tab are included.
381
660
  *
382
- * @returns The remembered names, most recent first, alongside ways to add to and empty them.
661
+ * @param scope - The library and style, such as `phosphor/bold`.
662
+ * @returns The recent icon names, most recent first, and functions to add to and clear them.
383
663
  */
384
- function useRecentIcons() {
385
- let $ = c(4), [recent, setRecent] = useState(readRecent), t0;
386
- $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = function remember(name) {
387
- setRecent(writeRecent(name));
388
- }, $[0] = t0) : t0 = $[0];
389
- let remember = t0, t1;
390
- $[1] === Symbol.for("react.memo_cache_sentinel") ? (t1 = function forget() {
391
- clearRecent(), setRecent([]);
392
- }, $[1] = t1) : t1 = $[1];
393
- let forget = t1, t2;
394
- return $[2] === recent ? t2 = $[3] : (t2 = {
664
+ function useRecentIcons(scope) {
665
+ let $ = c(10), t0;
666
+ $[0] === scope ? t0 = $[1] : (t0 = () => readRecent(scope), $[0] = scope, $[1] = t0);
667
+ let [recent, setRecent] = useState(t0), t1;
668
+ $[2] === scope ? t1 = $[3] : (t1 = function remember(name) {
669
+ setRecent(writeRecent(scope, name));
670
+ }, $[2] = scope, $[3] = t1);
671
+ let remember = t1, t2;
672
+ $[4] === scope ? t2 = $[5] : (t2 = function forget() {
673
+ clearRecent(scope), setRecent([]);
674
+ }, $[4] = scope, $[5] = t2);
675
+ let forget = t2, t3;
676
+ return $[6] !== forget || $[7] !== recent || $[8] !== remember ? (t3 = {
395
677
  recent,
396
678
  remember,
397
679
  forget
398
- }, $[2] = recent, $[3] = t2), t2;
680
+ }, $[6] = forget, $[7] = recent, $[8] = remember, $[9] = t3) : t3 = $[9], t3;
399
681
  }
400
682
  /**
401
- * Works out which icon a key press moves to, so the library can be walked without a mouse.
683
+ * Works out which icon a key press moves to.
402
684
  *
403
685
  * @param key - The key pressed.
404
- * @param current - Index of the icon the grid is on.
405
- * @param last - Index of the final icon in the results.
406
- * @returns The index moved to, or undefined when the key does not move.
686
+ * @param current - Index of the current icon.
687
+ * @param last - Index of the last icon.
688
+ * @returns The new index, or undefined when the key doesn't move.
407
689
  */
408
690
  function getNextIndex(key, current, last) {
409
691
  if (key === "ArrowRight") return current + 1;
@@ -414,12 +696,12 @@ function getNextIndex(key, current, last) {
414
696
  if (key === "End") return last;
415
697
  }
416
698
  /**
417
- * Works out which rows the grid builds for a given scroll position, so only the icons near the
418
- * viewport are mounted. The rows above and below stand in as padding, keeping the scrollbar honest.
699
+ * Works out which rows to render at a scroll position, so only icons near the viewport are mounted.
700
+ * Padding replaces the other rows, so the scrollbar stays the right size.
419
701
  *
420
- * @param scrollRow - Row the scrolling area has reached.
702
+ * @param scrollRow - The row scrolled to.
421
703
  * @param count - Number of icons in the results.
422
- * @returns The row the grid starts building at, the row it stops at, and the row count in total.
704
+ * @returns The first and last rows to render, and the total number of rows.
423
705
  */
424
706
  function getVisibleRows(scrollRow, count) {
425
707
  let totalRows = Math.ceil(count / 8), firstRow = Math.max(0, scrollRow - 8);
@@ -430,58 +712,56 @@ function getVisibleRows(scrollRow, count) {
430
712
  };
431
713
  }
432
714
  /**
433
- * Works out where the scrolling area sits to show a given icon in the middle, for opening on the
434
- * icon already chosen.
715
+ * Works out the scroll position that centres an icon, for opening on the selected icon.
435
716
  *
436
- * @param index - Index of the icon to show.
437
- * @returns The position to scroll to.
717
+ * @param index - Index of the icon.
718
+ * @returns The scroll position.
438
719
  */
439
- function getCentredScrollTop(index) {
720
+ function getCenteredScrollTop(index) {
440
721
  return Math.max(0, Math.floor(index / 8) * 44 - 174);
441
722
  }
442
723
  /**
443
- * Works out where the scrolling area moves to bring a given icon into view, staying put when the
444
- * icon already shows. Taken from the index rather than the icon's own cell, since Home and End can
445
- * land outside the rows that are mounted.
724
+ * Works out the scroll position that brings an icon into view, keeping the current one if it's
725
+ * already visible. It uses the index because Home and End can move to rows that aren't mounted.
446
726
  *
447
- * @param index - Index of the icon to reveal.
448
- * @param scrollTop - Position the scrolling area has reached.
449
- * @returns The position to scroll to.
727
+ * @param index - Index of the icon.
728
+ * @param scrollTop - The current scroll position.
729
+ * @returns The scroll position.
450
730
  */
451
731
  function getRevealedScrollTop(index, scrollTop) {
452
732
  let top = Math.floor(index / 8) * 44;
453
733
  return top < scrollTop ? top : top + 44 > scrollTop + 348 ? top + 44 - 348 : scrollTop;
454
734
  }
455
735
  function Picker(t0) {
456
- let $ = c(35), allowed, onSelect, props, selected;
457
- $[0] === t0 ? (allowed = $[1], onSelect = $[2], props = $[3], selected = $[4]) : ({allowed, selected, onSelect, ...props} = t0, $[0] = t0, $[1] = allowed, $[2] = onSelect, $[3] = props, $[4] = selected);
458
- let library = useIconLibrary(), icons = selectIcons(library ?? [], allowed), { recent, remember, forget } = useRecentIcons(), [search, setSearch] = useState(""), [scrollRow, setScrollRow] = useState(0), [movedIndex, setMovedIndex] = useState(), [scrolling, setScrolling] = useState(!1), scroller = useRef(null), activeCell = useRef(null), focusActiveCell = useRef(!1), scrollFrame = useRef(void 0), scrollStop = useRef(void 0), query = normaliseTerms(search), searching = isDefined(query), results = icons.filter((icon) => !searching || icon.terms.includes(query)), byName = new Map(icons.map(_temp)), recentIcons = searching ? [] : recent.flatMap((entry) => {
736
+ let $ = c(39), allowed, iconStyle, libraryName, onSelect, props, selected;
737
+ $[0] === t0 ? (allowed = $[1], iconStyle = $[2], libraryName = $[3], onSelect = $[4], props = $[5], selected = $[6]) : ({library: libraryName, iconStyle, allowed, selected, onSelect, ...props} = t0, $[0] = t0, $[1] = allowed, $[2] = iconStyle, $[3] = libraryName, $[4] = onSelect, $[5] = props, $[6] = selected);
738
+ let result = useIconLibrary(libraryName, iconStyle), library = result?.icons, icons = selectIcons(library ?? [], allowed), t1 = libraryName ?? "", t2 = iconStyle ?? "default", t3;
739
+ $[7] !== t1 || $[8] !== t2 ? (t3 = getIconSetKey(t1, t2), $[7] = t1, $[8] = t2, $[9] = t3) : t3 = $[9];
740
+ let { recent, remember, forget } = useRecentIcons(t3), [search, setSearch] = useState(""), [scrollRow, setScrollRow] = useState(0), [movedIndex, setMovedIndex] = useState(), [scrolling, setScrolling] = useState(!1), scroller = useRef(null), activeCell = useRef(null), focusIndex = useRef(void 0), scrollFrame = useRef(void 0), scrollStop = useRef(void 0), query = normalizeTerms(search), searching = isDefined(query), results = icons.filter((icon) => !searching || icon.terms.includes(query)), byName = new Map(icons.map(_temp)), recentIcons = searching ? [] : recent.flatMap((entry) => {
459
741
  let icon_1 = byName.get(entry);
460
742
  return isDefined(icon_1) ? [icon_1] : [];
461
- }), { hovered, handleHover, clearHover } = useIconTooltip(byName, scrolling), rows = getVisibleRows(scrollRow, results.length), selectedIndex = searching || !isDefined(selected) ? -1 : icons.findIndex((icon_2) => icon_2.name === selected), activeIndex = movedIndex ?? Math.max(0, selectedIndex);
743
+ }), { hovered, handleHover, clearHover } = useIconTooltip(scrolling), rows = getVisibleRows(scrollRow, results.length), selectedIndex = searching || !isDefined(selected) ? -1 : icons.findIndex((icon_2) => icon_2.name === selected), activeIndex = movedIndex ?? Math.max(0, selectedIndex);
462
744
  useEffect(() => {
463
745
  let element = scroller.current;
464
- isDefined(element) && selectedIndex !== -1 && (element.scrollTop = getCentredScrollTop(selectedIndex));
465
- }, [selectedIndex]);
466
- let t1, t2;
467
- $[5] === Symbol.for("react.memo_cache_sentinel") ? (t1 = () => () => {
746
+ isDefined(element) && selectedIndex !== -1 && (element.scrollTop = getCenteredScrollTop(selectedIndex));
747
+ }, [scroller, selectedIndex]);
748
+ let t4, t5;
749
+ $[10] === Symbol.for("react.memo_cache_sentinel") ? (t4 = () => () => {
468
750
  isDefined(scrollFrame.current) && cancelAnimationFrame(scrollFrame.current), isDefined(scrollStop.current) && clearTimeout(scrollStop.current);
469
- }, t2 = [], $[5] = t1, $[6] = t2) : (t1 = $[5], t2 = $[6]), useEffect(t1, t2);
470
- let t3;
471
- $[7] === Symbol.for("react.memo_cache_sentinel") ? (t3 = () => {
472
- focusActiveCell.current && (focusActiveCell.current = !1, isDefined(activeCell.current) && activeCell.current.focus());
473
- }, $[7] = t3) : t3 = $[7];
474
- let t4;
475
- $[8] === activeIndex ? t4 = $[9] : (t4 = [activeIndex], $[8] = activeIndex, $[9] = t4), useEffect(t3, t4);
476
- let t5;
477
- $[10] !== onSelect || $[11] !== remember ? (t5 = function handleSelect(icon_3) {
751
+ }, t5 = [], $[10] = t4, $[11] = t5) : (t4 = $[10], t5 = $[11]), useEffect(t4, t5);
752
+ let t6, t7;
753
+ $[12] === activeIndex ? (t6 = $[13], t7 = $[14]) : (t6 = () => {
754
+ focusIndex.current === activeIndex && (focusIndex.current = void 0, isDefined(activeCell.current) && activeCell.current.focus());
755
+ }, t7 = [activeIndex], $[12] = activeIndex, $[13] = t6, $[14] = t7), useEffect(t6, t7);
756
+ let t8;
757
+ $[15] !== onSelect || $[16] !== remember ? (t8 = function handleSelect(icon_3) {
478
758
  remember(icon_3.name), onSelect(icon_3);
479
- }, $[10] = onSelect, $[11] = remember, $[12] = t5) : t5 = $[12];
480
- let handleSelect = t5, t6;
481
- $[13] === Symbol.for("react.memo_cache_sentinel") ? (t6 = function clearSearch() {
759
+ }, $[15] = onSelect, $[16] = remember, $[17] = t8) : t8 = $[17];
760
+ let handleSelect = t8, t9;
761
+ $[18] === Symbol.for("react.memo_cache_sentinel") ? (t9 = function clearSearch() {
482
762
  setSearch(""), setMovedIndex(void 0);
483
- }, $[13] = t6) : t6 = $[13];
484
- let clearSearch = t6, handleSearch = function handleSearch(event) {
763
+ }, $[18] = t9) : t9 = $[18];
764
+ let clearSearch = t9, handleSearch = function handleSearch(event) {
485
765
  setSearch(event.currentTarget.value), setMovedIndex(void 0), isDefined(scroller.current) && (scroller.current.scrollTop = 0);
486
766
  }, selectActive = function selectActive() {
487
767
  let icon_4 = results[activeIndex];
@@ -511,20 +791,35 @@ function Picker(t0) {
511
791
  if (!isDefined(next)) return;
512
792
  event_1.preventDefault();
513
793
  let index = Math.min(Math.max(next, 0), last);
514
- focusActiveCell.current = !0, setMovedIndex(index);
794
+ focusIndex.current = index, setMovedIndex(index);
515
795
  let element_1 = scroller.current;
516
796
  isDefined(element_1) && (element_1.scrollTop = getRevealedScrollTop(index, element_1.scrollTop));
517
- }, t7;
518
- $[14] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(SearchIcon, { ...defaultIconProps }), $[14] = t7) : t7 = $[14];
519
- let t8;
520
- $[15] === library ? t8 = $[16] : (t8 = !isDefined(library) && /* @__PURE__ */ jsx(Flex, {
797
+ }, T0 = Dialog, T1 = Stack, t14 = !isDefined(result?.problem) && /* @__PURE__ */ jsx(TextInput, {
798
+ type: "search",
799
+ icon: /* @__PURE__ */ jsx(SearchIcon, {}),
800
+ placeholder: "Search by name or keyword",
801
+ value: search,
802
+ onChange: handleSearch,
803
+ onKeyDown: handleSearchKeyDown,
804
+ disabled: !isDefined(library) || !isDefined(icons),
805
+ "aria-label": "Search the icon library"
806
+ }), t15;
807
+ $[19] === result ? t15 = $[20] : (t15 = !isDefined(result) && /* @__PURE__ */ jsx(Flex, {
521
808
  align: "center",
522
809
  justify: "center",
523
810
  style: { height: 348 },
524
811
  children: /* @__PURE__ */ jsx(Spinner, { muted: !0 })
525
- }), $[15] = library, $[16] = t8);
526
- let t9;
527
- $[17] !== activeIndex || $[18] !== clearHover || $[19] !== clearSearch || $[20] !== handleGridKeyDown || $[21] !== handleHover || $[22] !== handleScroll || $[23] !== handleSelect || $[24] !== icons || $[25] !== library || $[26] !== results || $[27] !== rows || $[28] !== scroller || $[29] !== search || $[30] !== selected ? (t9 = isDefined(library) && /* @__PURE__ */ jsxs(Stack, {
812
+ }), $[19] = result, $[20] = t15);
813
+ let t16;
814
+ $[21] === result ? t16 = $[22] : (t16 = isDefined(result?.problem) && /* @__PURE__ */ jsx(Problem, { problem: result.problem }), $[21] = result, $[22] = t16);
815
+ let t17 = isDefined(recentIcons) && /* @__PURE__ */ jsx(Recent, {
816
+ icons: recentIcons,
817
+ selected,
818
+ onSelect: handleSelect,
819
+ onHover: handleHover,
820
+ onForget: forget,
821
+ onMouseLeave: clearHover
822
+ }), t18 = isDefined(library) && /* @__PURE__ */ jsxs(Stack, {
528
823
  gap: 3,
529
824
  children: [
530
825
  /* @__PURE__ */ jsx(Box, {
@@ -535,22 +830,23 @@ function Picker(t0) {
535
830
  muted: !0,
536
831
  children: [
537
832
  "All icons (",
538
- results.length.toLocaleString("en-GB"),
833
+ results.length.toLocaleString("en-US"),
539
834
  ")"
540
835
  ]
541
836
  })
542
837
  }),
543
- icons.length === 0 && /* @__PURE__ */ jsx(Flex, {
838
+ !isDefined(icons) && /* @__PURE__ */ jsx(Flex, {
544
839
  align: "center",
545
840
  justify: "center",
546
- style: { height: 348 },
841
+ paddingTop: 5,
842
+ paddingBottom: 6,
547
843
  children: /* @__PURE__ */ jsx(Text, {
548
844
  size: 1,
549
845
  muted: !0,
550
- children: "No icons are available to choose from."
846
+ children: "No icons available."
551
847
  })
552
848
  }),
553
- icons.length > 0 && /* @__PURE__ */ jsx(Library, {
849
+ isDefined(icons) && /* @__PURE__ */ jsx(Library, {
554
850
  ref: scroller,
555
851
  icons: results,
556
852
  rows,
@@ -559,105 +855,151 @@ function Picker(t0) {
559
855
  activeCell,
560
856
  search,
561
857
  onSelect: handleSelect,
858
+ onHover: handleHover,
562
859
  onClearSearch: clearSearch,
563
860
  onScroll: handleScroll,
564
- onMouseOver: handleHover,
565
861
  onMouseLeave: clearHover,
566
862
  onKeyDown: handleGridKeyDown
567
863
  })
568
864
  ]
569
- }), $[17] = activeIndex, $[18] = clearHover, $[19] = clearSearch, $[20] = handleGridKeyDown, $[21] = handleHover, $[22] = handleScroll, $[23] = handleSelect, $[24] = icons, $[25] = library, $[26] = results, $[27] = rows, $[28] = scroller, $[29] = search, $[30] = selected, $[31] = t9) : t9 = $[31];
570
- let t10 = hovered?.icon.name, t11;
571
- return $[32] !== hovered || $[33] !== t10 ? (t11 = /* @__PURE__ */ jsx(Tooltip, { hovered }, t10), $[32] = hovered, $[33] = t10, $[34] = t11) : t11 = $[34], /* @__PURE__ */ jsx(Dialog, {
865
+ }), t19 = hovered?.icon.name, t20;
866
+ $[23] !== hovered || $[24] !== query || $[25] !== t19 ? (t20 = /* @__PURE__ */ jsx(Tooltip, {
867
+ hovered,
868
+ query
869
+ }, t19), $[23] = hovered, $[24] = query, $[25] = t19, $[26] = t20) : t20 = $[26];
870
+ let t21;
871
+ $[27] !== T1 || $[28] !== t14 || $[29] !== t15 || $[30] !== t16 || $[31] !== t17 || $[32] !== t18 || $[33] !== t20 ? (t21 = /* @__PURE__ */ jsxs(T1, {
872
+ gap: 4,
873
+ padding: 4,
874
+ children: [
875
+ t14,
876
+ t15,
877
+ t16,
878
+ t17,
879
+ t18,
880
+ t20
881
+ ]
882
+ }), $[27] = T1, $[28] = t14, $[29] = t15, $[30] = t16, $[31] = t17, $[32] = t18, $[33] = t20, $[34] = t21) : t21 = $[34];
883
+ let t22;
884
+ return $[35] !== T0 || $[36] !== props || $[37] !== t21 ? (t22 = /* @__PURE__ */ jsx(T0, {
572
885
  header: "Select icon",
573
886
  width: 1,
574
887
  ...props,
575
- children: /* @__PURE__ */ jsxs(Stack, {
576
- gap: 4,
577
- padding: 4,
578
- children: [
579
- /* @__PURE__ */ jsx(TextInput, {
580
- icon: t7,
581
- placeholder: "Search by name or by what the icon depicts",
582
- value: search,
583
- onChange: handleSearch,
584
- onKeyDown: handleSearchKeyDown,
585
- disabled: !isDefined(library) || icons.length === 0,
586
- "aria-label": "Search the icon library"
587
- }),
588
- t8,
589
- isDefined(recentIcons) && /* @__PURE__ */ jsx(Recent, {
590
- icons: recentIcons,
591
- selected,
592
- onSelect: handleSelect,
593
- onForget: forget,
594
- onMouseOver: handleHover,
595
- onMouseLeave: clearHover
596
- }),
597
- t9,
598
- t11
599
- ]
600
- })
601
- });
888
+ children: t21
889
+ }), $[35] = T0, $[36] = props, $[37] = t21, $[38] = t22) : t22 = $[38], t22;
602
890
  }
603
891
  function _temp(icon_0) {
604
892
  return [icon_0.name, icon_0];
605
893
  }
606
- /** Type name of the icon object. */
894
+ function SquareDashedIcon(props) {
895
+ let $ = c(14), t0, t1, t10, t11, t2, t3, t4, t5, t6, t7, t8, t9;
896
+ $[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]);
897
+ let t12;
898
+ return $[12] === props ? t12 = $[13] : (t12 = /* @__PURE__ */ jsxs("svg", {
899
+ xmlns: "http://www.w3.org/2000/svg",
900
+ width: 24,
901
+ height: 24,
902
+ viewBox: "0 0 24 24",
903
+ fill: "none",
904
+ stroke: "currentColor",
905
+ strokeWidth: 2,
906
+ strokeLinecap: "round",
907
+ strokeLinejoin: "round",
908
+ ...props,
909
+ children: [
910
+ t0,
911
+ t1,
912
+ t2,
913
+ t3,
914
+ t4,
915
+ t5,
916
+ t6,
917
+ t7,
918
+ t8,
919
+ t9,
920
+ t10,
921
+ t11
922
+ ]
923
+ }), $[12] = props, $[13] = t12), t12;
924
+ }
925
+ /**
926
+ * Resolves the library, style, and icons a field offers. A field that names a library replaces all three of the
927
+ * plugin's, and one that doesn't uses the plugin's.
928
+ *
929
+ * @param options - The field's options.
930
+ * @param config - The plugin configuration.
931
+ * @returns The field's settings, whose library is undefined when neither the field nor the plugin names one.
932
+ */
933
+ function resolveIconOptions(options, config) {
934
+ let { library, style, icons } = (isDefined(options?.library) ? options : config) ?? {};
935
+ return {
936
+ library,
937
+ style,
938
+ icons
939
+ };
940
+ }
941
+ /** Schema type name of the icon object. */
607
942
  const iconTypeName = "icon";
608
943
  /**
609
- * Creates the input an icon field is drawn with, holding the icons the plugin was configured with so
610
- * a field naming its own replaces them rather than adding to them.
944
+ * Creates the icon field input, bound to the plugin's configuration. A field's own icons replace the
945
+ * plugin's rather than adding to them.
611
946
  *
612
- * @param icons - Icons every field offers, or nothing to offer them all.
947
+ * @param config - The plugin configuration.
613
948
  * @returns The input component.
614
949
  */
615
- function createInput(icons) {
950
+ function createInput(config) {
616
951
  function Input(t0) {
617
- let $ = c(29), { id, schemaType, value, onChange, readOnly } = t0, { name, node } = value ?? {}, [open, setOpen] = useState(!1), allowed = schemaType.options?.icons ?? icons, selectedNode = resolveIconNode(node), selectedLabel = isDefined(name) ? convertCase(name, "sentence") : void 0, t1;
618
- $[0] !== onChange || $[1] !== setOpen ? (t1 = function handleSelect(icon) {
952
+ let $ = c(35), { id, schemaType, value, onChange, readOnly } = t0, { library: storedLibrary, name, node } = value ?? {}, [open, setOpen] = useState(!1), t1;
953
+ $[0] === schemaType.options ? t1 = $[1] : (t1 = resolveIconOptions(schemaType.options, config), $[0] = schemaType.options, $[1] = t1);
954
+ let { library, style, icons } = t1, selectedDrawing = resolveIconDrawing(node), selectedName = storedLibrary === library ? name : void 0, selectedLabel = isDefined(name) ? convertCase(name, "title") : void 0, t2;
955
+ $[2] !== library || $[3] !== onChange || $[4] !== setOpen || $[5] !== style ? (t2 = function handleSelect(icon) {
619
956
  onChange([
620
957
  setIfMissing({ _type: iconTypeName }),
958
+ isDefined(library) ? set(library, ["library"]) : unset(["library"]),
959
+ isDefined(style) ? set(style, ["style"]) : unset(["style"]),
621
960
  set(icon.name, ["name"]),
622
- set(serialiseIconNode(icon.node), ["node"])
961
+ set(serializeIconDrawing(icon.drawing), ["node"])
623
962
  ]), setOpen(!1);
624
- }, $[0] = onChange, $[1] = setOpen, $[2] = t1) : t1 = $[2];
625
- let handleSelect = t1, T0 = Flex, T1 = Button, t5 = isDefined(selectedNode) ? /* @__PURE__ */ jsx(Drawing, {
626
- node: selectedNode,
627
- ...defaultIconProps
628
- }) : void 0, t6 = selectedLabel ?? "Select icon", t7;
629
- $[3] === setOpen ? t7 = $[4] : (t7 = () => {
963
+ }, $[2] = library, $[3] = onChange, $[4] = setOpen, $[5] = style, $[6] = t2) : t2 = $[6];
964
+ let handleSelect = t2, T0 = Flex, T1 = Button, t6 = isDefined(selectedDrawing) ? /* @__PURE__ */ jsx(Drawing, {
965
+ drawing: selectedDrawing,
966
+ width: "1em",
967
+ height: "1em"
968
+ }) : /* @__PURE__ */ jsx(SquareDashedIcon, { ...defaultIconProps }), t7 = selectedLabel ?? "Select icon", t8;
969
+ $[7] === setOpen ? t8 = $[8] : (t8 = () => {
630
970
  setOpen(!0);
631
- }, $[3] = setOpen, $[4] = t7);
632
- let t8 = isDefined(selectedLabel) ? `${selectedLabel} currently selected` : "Select icon", t9;
633
- $[5] !== T1 || $[6] !== id || $[7] !== readOnly || $[8] !== t5 || $[9] !== t6 || $[10] !== t7 || $[11] !== t8 ? (t9 = /* @__PURE__ */ jsx(T1, {
971
+ }, $[7] = setOpen, $[8] = t8);
972
+ let t9 = isDefined(selectedLabel) ? `${selectedLabel} currently selected` : "Select icon", t10;
973
+ $[9] !== T1 || $[10] !== id || $[11] !== readOnly || $[12] !== t6 || $[13] !== t7 || $[14] !== t8 || $[15] !== t9 ? (t10 = /* @__PURE__ */ jsx(T1, {
634
974
  id,
635
975
  type: "button",
636
976
  mode: "ghost",
637
- icon: t5,
638
- text: t6,
977
+ icon: t6,
978
+ text: t7,
639
979
  disabled: readOnly,
640
- onClick: t7,
641
- "aria-label": t8
642
- }), $[5] = T1, $[6] = id, $[7] = readOnly, $[8] = t5, $[9] = t6, $[10] = t7, $[11] = t8, $[12] = t9) : t9 = $[12];
643
- let t10;
644
- $[13] !== onChange || $[14] !== readOnly || $[15] !== value ? (t10 = isDefined(value) && /* @__PURE__ */ jsx(Button, {
980
+ onClick: t8,
981
+ "aria-label": t9
982
+ }), $[9] = T1, $[10] = id, $[11] = readOnly, $[12] = t6, $[13] = t7, $[14] = t8, $[15] = t9, $[16] = t10) : t10 = $[16];
983
+ let t11;
984
+ $[17] !== onChange || $[18] !== readOnly || $[19] !== value ? (t11 = isDefined(value) && /* @__PURE__ */ jsx(Button, {
645
985
  type: "button",
646
986
  mode: "ghost",
647
987
  tone: "critical",
648
- icon: /* @__PURE__ */ jsx(CloseIcon, { ...defaultIconProps }),
988
+ icon: /* @__PURE__ */ jsx(CloseIcon, {}),
649
989
  text: "Clear",
650
990
  disabled: readOnly,
651
991
  onClick: () => {
652
992
  onChange(unset());
653
993
  },
654
- "aria-label": "Clear the currently selected icon."
655
- }), $[13] = onChange, $[14] = readOnly, $[15] = value, $[16] = t10) : t10 = $[16];
656
- let t11;
657
- $[17] !== allowed || $[18] !== handleSelect || $[19] !== id || $[20] !== name || $[21] !== open || $[22] !== setOpen ? (t11 = open && /* @__PURE__ */ jsx(Picker, {
994
+ "aria-label": "Clear the selected icon"
995
+ }), $[17] = onChange, $[18] = readOnly, $[19] = value, $[20] = t11) : t11 = $[20];
996
+ let t12;
997
+ $[21] !== handleSelect || $[22] !== icons || $[23] !== id || $[24] !== library || $[25] !== open || $[26] !== selectedName || $[27] !== setOpen || $[28] !== style ? (t12 = open && /* @__PURE__ */ jsx(Picker, {
658
998
  id: `${id}-library`,
659
- allowed,
660
- selected: name,
999
+ library,
1000
+ iconStyle: style,
1001
+ allowed: icons,
1002
+ selected: selectedName,
661
1003
  onSelect: handleSelect,
662
1004
  onClose: () => {
663
1005
  setOpen(!1);
@@ -665,101 +1007,53 @@ function createInput(icons) {
665
1007
  onClickOutside: () => {
666
1008
  setOpen(!1);
667
1009
  }
668
- }), $[17] = allowed, $[18] = handleSelect, $[19] = id, $[20] = name, $[21] = open, $[22] = setOpen, $[23] = t11) : t11 = $[23];
669
- let t12;
670
- return $[24] !== T0 || $[25] !== t10 || $[26] !== t11 || $[27] !== t9 ? (t12 = /* @__PURE__ */ jsxs(T0, {
1010
+ }), $[21] = handleSelect, $[22] = icons, $[23] = id, $[24] = library, $[25] = open, $[26] = selectedName, $[27] = setOpen, $[28] = style, $[29] = t12) : t12 = $[29];
1011
+ let t13;
1012
+ return $[30] !== T0 || $[31] !== t10 || $[32] !== t11 || $[33] !== t12 ? (t13 = /* @__PURE__ */ jsxs(T0, {
671
1013
  gap: 2,
672
1014
  children: [
673
- t9,
674
1015
  t10,
675
- t11
1016
+ t11,
1017
+ t12
676
1018
  ]
677
- }), $[24] = T0, $[25] = t10, $[26] = t11, $[27] = t9, $[28] = t12) : t12 = $[28], t12;
1019
+ }), $[30] = T0, $[31] = t10, $[32] = t11, $[33] = t12, $[34] = t13) : t13 = $[34], t13;
678
1020
  }
679
1021
  return Input;
680
1022
  }
681
- function SquareDashedIcon(props) {
682
- let $ = c(14), t0, t1, t10, t11, t2, t3, t4, t5, t6, t7, t8, t9;
683
- $[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]);
684
- let t12;
685
- return $[12] === props ? t12 = $[13] : (t12 = /* @__PURE__ */ jsxs("svg", {
686
- xmlns: "http://www.w3.org/2000/svg",
687
- width: 24,
688
- height: 24,
689
- viewBox: "0 0 24 24",
690
- fill: "none",
691
- stroke: "currentColor",
692
- strokeWidth: 2,
693
- strokeLinecap: "round",
694
- strokeLinejoin: "round",
695
- ...props,
696
- children: [
697
- t0,
698
- t1,
699
- t2,
700
- t3,
701
- t4,
702
- t5,
703
- t6,
704
- t7,
705
- t8,
706
- t9,
707
- t10,
708
- t11
709
- ]
710
- }), $[12] = props, $[13] = t12), t12;
711
- }
712
- /**
713
- * Creates a Sanity Studio icon component from an SVG icon.
714
- *
715
- * @param icon - The icon component to wrap.
716
- * @param props - Additional props merged over the default properties a Studio icon is drawn with.
717
- * @returns A Studio icon component.
718
- */
719
- function createSanityIcon(icon, props) {
720
- let Icon = icon;
721
- function SanityIcon() {
722
- return createElement(Icon, {
723
- ...defaultIconProps,
724
- ...props
725
- });
726
- }
727
- return SanityIcon;
728
- }
729
1023
  /**
730
- * Builds the media a stored icon previews with, so a document lists the drawing an author chose
731
- * rather than one placeholder standing in for every icon alike.
1024
+ * Creates preview media for a stored icon, so each document shows its own icon instead of a placeholder.
732
1025
  *
733
1026
  * @param value - The stored icon.
734
1027
  * @returns The media component, or undefined when nothing readable was stored.
735
1028
  * @public
736
1029
  */
737
1030
  function createIconPreview(value) {
738
- let node = resolveIconNode(value?.node);
739
- if (isDefined(node)) return function IconPreview() {
1031
+ let drawing = resolveIconDrawing(value?.node);
1032
+ if (isDefined(drawing)) return function IconPreview() {
740
1033
  return createElement(Drawing, {
741
- node,
742
- ...defaultIconProps
1034
+ drawing,
1035
+ width: "1em",
1036
+ height: "1em"
743
1037
  });
744
1038
  };
745
1039
  }
746
1040
  /**
747
- * Creates the object type an icon is stored as, offering the icons it is given to every field that
748
- * does not name its own.
1041
+ * Creates the icon object type, offering the plugin's icons to fields that don't set their own.
749
1042
  *
750
- * @param icons - Icons every field offers, or nothing to offer them all.
751
- * @returns An object type definition for a stored icon.
1043
+ * @param config - The plugin configuration.
1044
+ * @returns The icon type.
752
1045
  */
753
- function createIconType(icons) {
1046
+ function createIconType(config) {
754
1047
  return defineType({
755
1048
  name: iconTypeName,
756
1049
  type: "object",
757
1050
  icon: createSanityIcon(SquareDashedIcon),
758
- description: "Icon chosen from the Lucide library.",
1051
+ description: "Icon chosen from an icon library.",
759
1052
  components: {
760
1053
  field: Field,
761
- input: createInput(icons)
1054
+ input: createInput(config)
762
1055
  },
1056
+ validation: (rule) => rule.custom((value) => !isDefined(value?.name) || isDefined(value.node) ? !0 : "Select the icon again to save its drawing."),
763
1057
  preview: {
764
1058
  select: {
765
1059
  name: "name",
@@ -768,7 +1062,7 @@ function createIconType(icons) {
768
1062
  prepare(selection) {
769
1063
  let { name, node } = selection;
770
1064
  return {
771
- title: isDefined(name) ? convertCase(name, "sentence") : "No icon",
1065
+ title: isDefined(name) ? convertCase(name, "title") : "No icon",
772
1066
  media: createIconPreview({
773
1067
  _type: iconTypeName,
774
1068
  name,
@@ -777,34 +1071,43 @@ function createIconType(icons) {
777
1071
  };
778
1072
  }
779
1073
  },
780
- fields: [defineField({
781
- name: "name",
782
- type: "string",
783
- description: "Name of the chosen icon in the library it was taken from."
784
- }), defineField({
785
- name: "node",
786
- type: "text",
787
- description: "Shapes the icon is drawn from, written when the icon is chosen."
788
- })]
1074
+ fields: [
1075
+ defineField({
1076
+ name: "library",
1077
+ type: "string",
1078
+ description: "Identifier of the library the icon came from."
1079
+ }),
1080
+ defineField({
1081
+ name: "style",
1082
+ type: "string",
1083
+ description: "Identifier of the library style the icon came from."
1084
+ }),
1085
+ defineField({
1086
+ name: "name",
1087
+ type: "string",
1088
+ description: "Name of the icon in its library."
1089
+ }),
1090
+ defineField({
1091
+ name: "node",
1092
+ type: "text",
1093
+ description: "Shapes the icon is drawn from, saved when it's chosen."
1094
+ })
1095
+ ]
789
1096
  });
790
1097
  }
791
- /** The plugin as Sanity defines it, wrapped below so that its configuration can genuinely be left out. */
792
- const plugin = definePlugin((config) => {
793
- let { icons } = config;
794
- return {
795
- name: "@driftime/sanity-plugin-icon",
796
- schema: { types: [createIconType(icons)] }
797
- };
798
- });
1098
+ const plugin = definePlugin((config) => ({
1099
+ name: pluginName,
1100
+ schema: { types: [createIconType(config)] }
1101
+ }));
799
1102
  /**
800
- * Creates the icon field type for Sanity Studio, offering the Lucide library through a searchable
801
- * picker and storing the chosen drawing ready to render.
1103
+ * Adds an icon field type to Sanity Studio, with a searchable picker for the installed icon libraries that
1104
+ * stores the chosen icon's drawing.
802
1105
  *
803
- * @param config - Plugin configuration.
804
- * @returns Sanity plugin definition.
1106
+ * @param config - The plugin configuration.
1107
+ * @returns The plugin.
805
1108
  * @public
806
1109
  */
807
- function iconPlugin(config = {}) {
1110
+ function iconPlugin(config) {
808
1111
  return plugin(config);
809
1112
  }
810
1113
  export { createIconPreview, iconPlugin };