@schandlergarcia/sf-web-components 1.0.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 (195) hide show
  1. package/.a4drules/skills/building-data-visualization/SKILL.md +72 -0
  2. package/.a4drules/skills/building-data-visualization/implementation/bar-line-chart.md +316 -0
  3. package/.a4drules/skills/building-data-visualization/implementation/dashboard-layout.md +189 -0
  4. package/.a4drules/skills/building-data-visualization/implementation/donut-chart.md +181 -0
  5. package/.a4drules/skills/building-data-visualization/implementation/stat-card.md +150 -0
  6. package/.a4drules/skills/building-react-components/SKILL.md +96 -0
  7. package/.a4drules/skills/building-react-components/implementation/component.md +78 -0
  8. package/.a4drules/skills/building-react-components/implementation/header-footer.md +132 -0
  9. package/.a4drules/skills/building-react-components/implementation/page.md +93 -0
  10. package/.a4drules/skills/configuring-csp-trusted-sites/SKILL.md +90 -0
  11. package/.a4drules/skills/configuring-csp-trusted-sites/implementation/metadata-format.md +281 -0
  12. package/.a4drules/skills/configuring-webapp-metadata/SKILL.md +158 -0
  13. package/.a4drules/skills/creating-webapp/SKILL.md +140 -0
  14. package/.a4drules/skills/deploying-to-salesforce/SKILL.md +226 -0
  15. package/.a4drules/skills/implementing-file-upload/SKILL.md +396 -0
  16. package/.a4drules/skills/installing-webapp-features/SKILL.md +210 -0
  17. package/.a4drules/skills/managing-agentforce-conversation-client/SKILL.md +186 -0
  18. package/.a4drules/skills/managing-agentforce-conversation-client/references/constraints.md +134 -0
  19. package/.a4drules/skills/managing-agentforce-conversation-client/references/examples.md +132 -0
  20. package/.a4drules/skills/managing-agentforce-conversation-client/references/style-tokens.md +101 -0
  21. package/.a4drules/skills/managing-agentforce-conversation-client/references/troubleshooting.md +57 -0
  22. package/.a4drules/skills/using-salesforce-data/SKILL.md +363 -0
  23. package/.a4drules/skills/using-salesforce-data/graphql-search.sh +139 -0
  24. package/.a4drules/webapp-data.md +353 -0
  25. package/.a4drules/webapp-ui.md +16 -0
  26. package/README.md +124 -0
  27. package/dist/components/library/cards/ActionList.js +27 -0
  28. package/dist/components/library/cards/ActionList.js.map +1 -0
  29. package/dist/components/library/cards/ActivityCard.js +40 -0
  30. package/dist/components/library/cards/ActivityCard.js.map +1 -0
  31. package/dist/components/library/cards/BaseCard.js +89 -0
  32. package/dist/components/library/cards/BaseCard.js.map +1 -0
  33. package/dist/components/library/cards/CalloutCard.js +28 -0
  34. package/dist/components/library/cards/CalloutCard.js.map +1 -0
  35. package/dist/components/library/cards/ChartCard.js +79 -0
  36. package/dist/components/library/cards/ChartCard.js.map +1 -0
  37. package/dist/components/library/cards/FeedPanel.js +38 -0
  38. package/dist/components/library/cards/FeedPanel.js.map +1 -0
  39. package/dist/components/library/cards/ListCard.js +112 -0
  40. package/dist/components/library/cards/ListCard.js.map +1 -0
  41. package/dist/components/library/cards/MetricCard.js +86 -0
  42. package/dist/components/library/cards/MetricCard.js.map +1 -0
  43. package/dist/components/library/cards/MetricsStrip.js +60 -0
  44. package/dist/components/library/cards/MetricsStrip.js.map +1 -0
  45. package/dist/components/library/cards/SectionCard.js +59 -0
  46. package/dist/components/library/cards/SectionCard.js.map +1 -0
  47. package/dist/components/library/cards/StatusCard.js +137 -0
  48. package/dist/components/library/cards/StatusCard.js.map +1 -0
  49. package/dist/components/library/cards/TableCard.js +244 -0
  50. package/dist/components/library/cards/TableCard.js.map +1 -0
  51. package/dist/components/library/cards/WidgetCard.js +60 -0
  52. package/dist/components/library/cards/WidgetCard.js.map +1 -0
  53. package/dist/components/library/charts/D3Chart.js +74 -0
  54. package/dist/components/library/charts/D3Chart.js.map +1 -0
  55. package/dist/components/library/charts/D3ChartTemplates.js +44 -0
  56. package/dist/components/library/charts/D3ChartTemplates.js.map +1 -0
  57. package/dist/components/library/charts/GeoMap.js +229 -0
  58. package/dist/components/library/charts/GeoMap.js.map +1 -0
  59. package/dist/components/library/chat/ChatBar.js +194 -0
  60. package/dist/components/library/chat/ChatBar.js.map +1 -0
  61. package/dist/components/library/chat/ChatInput.js +67 -0
  62. package/dist/components/library/chat/ChatInput.js.map +1 -0
  63. package/dist/components/library/chat/ChatMessage.js +112 -0
  64. package/dist/components/library/chat/ChatMessage.js.map +1 -0
  65. package/dist/components/library/chat/ChatMessageList.js +50 -0
  66. package/dist/components/library/chat/ChatMessageList.js.map +1 -0
  67. package/dist/components/library/chat/ChatPanel.js +77 -0
  68. package/dist/components/library/chat/ChatPanel.js.map +1 -0
  69. package/dist/components/library/chat/ChatSuggestions.js +22 -0
  70. package/dist/components/library/chat/ChatSuggestions.js.map +1 -0
  71. package/dist/components/library/chat/ChatToolCall.js +87 -0
  72. package/dist/components/library/chat/ChatToolCall.js.map +1 -0
  73. package/dist/components/library/chat/ChatTypingIndicator.js +20 -0
  74. package/dist/components/library/chat/ChatTypingIndicator.js.map +1 -0
  75. package/dist/components/library/chat/ChatWelcome.js +24 -0
  76. package/dist/components/library/chat/ChatWelcome.js.map +1 -0
  77. package/dist/components/library/chat/useChatState.js +77 -0
  78. package/dist/components/library/chat/useChatState.js.map +1 -0
  79. package/dist/components/library/data/DataModeProvider.js +47 -0
  80. package/dist/components/library/data/DataModeProvider.js.map +1 -0
  81. package/dist/components/library/data/DataModeToggle.js +28 -0
  82. package/dist/components/library/data/DataModeToggle.js.map +1 -0
  83. package/dist/components/library/data/filterUtils.js +71 -0
  84. package/dist/components/library/data/filterUtils.js.map +1 -0
  85. package/dist/components/library/data/useDataSource.js +13 -0
  86. package/dist/components/library/data/useDataSource.js.map +1 -0
  87. package/dist/components/library/data/usePageFilters.js +46 -0
  88. package/dist/components/library/data/usePageFilters.js.map +1 -0
  89. package/dist/components/library/filters/FilterBar.js +89 -0
  90. package/dist/components/library/filters/FilterBar.js.map +1 -0
  91. package/dist/components/library/filters/SearchFilter.js +44 -0
  92. package/dist/components/library/filters/SearchFilter.js.map +1 -0
  93. package/dist/components/library/filters/SelectFilter.js +44 -0
  94. package/dist/components/library/filters/SelectFilter.js.map +1 -0
  95. package/dist/components/library/filters/ToggleFilter.js +48 -0
  96. package/dist/components/library/filters/ToggleFilter.js.map +1 -0
  97. package/dist/components/library/forms/FormField.js +256 -0
  98. package/dist/components/library/forms/FormField.js.map +1 -0
  99. package/dist/components/library/forms/FormModal.js +161 -0
  100. package/dist/components/library/forms/FormModal.js.map +1 -0
  101. package/dist/components/library/forms/FormRenderer.js +32 -0
  102. package/dist/components/library/forms/FormRenderer.js.map +1 -0
  103. package/dist/components/library/forms/FormSection.js +49 -0
  104. package/dist/components/library/forms/FormSection.js.map +1 -0
  105. package/dist/components/library/forms/useFormState.js +85 -0
  106. package/dist/components/library/forms/useFormState.js.map +1 -0
  107. package/dist/components/library/heroui/Accordion.js +12 -0
  108. package/dist/components/library/heroui/Accordion.js.map +1 -0
  109. package/dist/components/library/heroui/Alert.js +12 -0
  110. package/dist/components/library/heroui/Alert.js.map +1 -0
  111. package/dist/components/library/heroui/Badge.js +12 -0
  112. package/dist/components/library/heroui/Badge.js.map +1 -0
  113. package/dist/components/library/heroui/Breadcrumbs.js +12 -0
  114. package/dist/components/library/heroui/Breadcrumbs.js.map +1 -0
  115. package/dist/components/library/heroui/Button.js +18 -0
  116. package/dist/components/library/heroui/Button.js.map +1 -0
  117. package/dist/components/library/heroui/Card.js +12 -0
  118. package/dist/components/library/heroui/Card.js.map +1 -0
  119. package/dist/components/library/heroui/Drawer.js +12 -0
  120. package/dist/components/library/heroui/Drawer.js.map +1 -0
  121. package/dist/components/library/heroui/Dropdown.js +12 -0
  122. package/dist/components/library/heroui/Dropdown.js.map +1 -0
  123. package/dist/components/library/heroui/Input.js +10 -0
  124. package/dist/components/library/heroui/Input.js.map +1 -0
  125. package/dist/components/library/heroui/Kbd.js +12 -0
  126. package/dist/components/library/heroui/Kbd.js.map +1 -0
  127. package/dist/components/library/heroui/Meter.js +12 -0
  128. package/dist/components/library/heroui/Meter.js.map +1 -0
  129. package/dist/components/library/heroui/Modal.js +12 -0
  130. package/dist/components/library/heroui/Modal.js.map +1 -0
  131. package/dist/components/library/heroui/Pagination.js +12 -0
  132. package/dist/components/library/heroui/Pagination.js.map +1 -0
  133. package/dist/components/library/heroui/ProgressBar.js +12 -0
  134. package/dist/components/library/heroui/ProgressBar.js.map +1 -0
  135. package/dist/components/library/heroui/ProgressCircle.js +12 -0
  136. package/dist/components/library/heroui/ProgressCircle.js.map +1 -0
  137. package/dist/components/library/heroui/ScrollShadow.js +12 -0
  138. package/dist/components/library/heroui/ScrollShadow.js.map +1 -0
  139. package/dist/components/library/heroui/Select.js +12 -0
  140. package/dist/components/library/heroui/Select.js.map +1 -0
  141. package/dist/components/library/heroui/Separator.js +12 -0
  142. package/dist/components/library/heroui/Separator.js.map +1 -0
  143. package/dist/components/library/heroui/Skeleton.js +12 -0
  144. package/dist/components/library/heroui/Skeleton.js.map +1 -0
  145. package/dist/components/library/heroui/Tabs.js +12 -0
  146. package/dist/components/library/heroui/Tabs.js.map +1 -0
  147. package/dist/components/library/heroui/Toast.js +13 -0
  148. package/dist/components/library/heroui/Toast.js.map +1 -0
  149. package/dist/components/library/heroui/Toggle.js +12 -0
  150. package/dist/components/library/heroui/Toggle.js.map +1 -0
  151. package/dist/components/library/heroui/Tooltip.js +12 -0
  152. package/dist/components/library/heroui/Tooltip.js.map +1 -0
  153. package/dist/components/library/layout/PageContainer.js +9 -0
  154. package/dist/components/library/layout/PageContainer.js.map +1 -0
  155. package/dist/components/library/skeletons/CardSkeleton.js +29 -0
  156. package/dist/components/library/skeletons/CardSkeleton.js.map +1 -0
  157. package/dist/components/library/theme/AppThemeProvider.js +55 -0
  158. package/dist/components/library/theme/AppThemeProvider.js.map +1 -0
  159. package/dist/components/library/theme/tokens.js +55 -0
  160. package/dist/components/library/theme/tokens.js.map +1 -0
  161. package/dist/components/library/ui/Avatar.js +33 -0
  162. package/dist/components/library/ui/Avatar.js.map +1 -0
  163. package/dist/components/library/ui/Button.js +50 -0
  164. package/dist/components/library/ui/Button.js.map +1 -0
  165. package/dist/components/library/ui/Card.js +21 -0
  166. package/dist/components/library/ui/Card.js.map +1 -0
  167. package/dist/components/library/ui/Chip.js +31 -0
  168. package/dist/components/library/ui/Chip.js.map +1 -0
  169. package/dist/components/library/ui/Container.js +42 -0
  170. package/dist/components/library/ui/Container.js.map +1 -0
  171. package/dist/components/library/ui/EmptyState.js +27 -0
  172. package/dist/components/library/ui/EmptyState.js.map +1 -0
  173. package/dist/components/library/ui/Input.js +22 -0
  174. package/dist/components/library/ui/Input.js.map +1 -0
  175. package/dist/components/library/ui/Spinner.js +64 -0
  176. package/dist/components/library/ui/Spinner.js.map +1 -0
  177. package/dist/components/library/ui/Text.js +43 -0
  178. package/dist/components/library/ui/Text.js.map +1 -0
  179. package/dist/components/library/ui/Toggle.js +47 -0
  180. package/dist/components/library/ui/Toggle.js.map +1 -0
  181. package/dist/components/workspace/ComponentRegistry.js +231 -0
  182. package/dist/components/workspace/ComponentRegistry.js.map +1 -0
  183. package/dist/index.js +185 -0
  184. package/dist/index.js.map +1 -0
  185. package/dist/lib/utils.js +9 -0
  186. package/dist/lib/utils.js.map +1 -0
  187. package/dist/node_modules/clsx/dist/clsx.js +17 -0
  188. package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
  189. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +2925 -0
  190. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  191. package/package.json +81 -0
  192. package/scripts/get-graphql-schema.mjs +68 -0
  193. package/scripts/reset-command-center.sh +358 -0
  194. package/scripts/rewrite-e2e-assets.mjs +23 -0
  195. package/scripts/validate-dashboard.sh +290 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatState.js","sources":["../../../../src/components/library/chat/useChatState.jsx"],"sourcesContent":["import { useState, useCallback, useRef } from \"react\";\n\nlet _nextId = 1;\nfunction uid() {\n return `msg-${Date.now()}-${_nextId++}`;\n}\n\n/**\n * Core state management hook for AI chat.\n *\n * @param {Object} options\n * @param {Array} options.initialMessages — seed the conversation\n * @param {Function} options.onSend — async (userMessage, allMessages) => assistantMessage | void\n * Return a message object to add it, or use appendChunk / addMessage for streaming.\n *\n * @returns {Object}\n *\n * @example\n * const chat = useChatState({\n * onSend: async (msg, history) => {\n * const res = await fetch(\"/api/chat\", { method: \"POST\", body: JSON.stringify({ messages: history }) });\n * const data = await res.json();\n * return { role: \"assistant\", content: data.reply, components: data.components };\n * },\n * });\n */\nexport default function useChatState({ initialMessages = [], onSend } = {}) {\n const [messages, setMessages] = useState(() =>\n initialMessages.map((m) => ({ id: uid(), timestamp: new Date().toISOString(), ...m }))\n );\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState(null);\n const onSendRef = useRef(onSend);\n onSendRef.current = onSend;\n\n const addMessage = useCallback((msg) => {\n const full = { id: uid(), timestamp: new Date().toISOString(), ...msg };\n setMessages((prev) => [...prev, full]);\n return full.id;\n }, []);\n\n const updateMessage = useCallback((id, updates) => {\n setMessages((prev) =>\n prev.map((m) => (m.id === id ? { ...m, ...updates } : m))\n );\n }, []);\n\n const appendChunk = useCallback((id, chunk) => {\n setMessages((prev) =>\n prev.map((m) =>\n m.id === id ? { ...m, content: (m.content ?? \"\") + chunk } : m\n )\n );\n }, []);\n\n const removeMessage = useCallback((id) => {\n setMessages((prev) => prev.filter((m) => m.id !== id));\n }, []);\n\n const clearMessages = useCallback(() => {\n setMessages([]);\n setError(null);\n }, []);\n\n const sendMessage = useCallback(\n async (content, extra = {}) => {\n if (!content?.trim()) return;\n setError(null);\n\n const userMsg = { role: \"user\", content: content.trim(), ...extra };\n const userId = uid();\n const fullUser = { ...userMsg, id: userId, timestamp: new Date().toISOString() };\n\n setMessages((prev) => [...prev, fullUser]);\n setIsLoading(true);\n\n try {\n const allMsgs = [...messages, fullUser];\n const result = await onSendRef.current?.(fullUser, allMsgs, {\n addMessage,\n updateMessage,\n appendChunk,\n setStreaming: setIsStreaming,\n });\n\n if (result && typeof result === \"object\" && result.role) {\n addMessage(result);\n }\n } catch (err) {\n setError(err?.message ?? \"Failed to send message\");\n addMessage({\n role: \"system\",\n content: err?.message ?? \"Something went wrong. Please try again.\",\n isError: true,\n });\n } finally {\n setIsLoading(false);\n setIsStreaming(false);\n }\n },\n [messages, addMessage, updateMessage, appendChunk]\n );\n\n const retryLast = useCallback(() => {\n const lastUser = [...messages].reverse().find((m) => m.role === \"user\");\n if (!lastUser) return;\n\n const idx = messages.lastIndexOf(lastUser);\n setMessages(messages.slice(0, idx));\n setError(null);\n\n sendMessage(lastUser.content);\n }, [messages, sendMessage]);\n\n return {\n messages,\n isLoading,\n isStreaming,\n error,\n sendMessage,\n addMessage,\n updateMessage,\n appendChunk,\n removeMessage,\n clearMessages,\n retryLast,\n setError,\n };\n}\n"],"names":["_nextId","uid","useChatState","initialMessages","onSend","messages","setMessages","useState","m","isLoading","setIsLoading","isStreaming","setIsStreaming","error","setError","onSendRef","useRef","addMessage","useCallback","msg","full","prev","updateMessage","id","updates","appendChunk","chunk","removeMessage","clearMessages","sendMessage","content","extra","userMsg","userId","fullUser","allMsgs","result","err","retryLast","lastUser","idx"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { jsx as m } from "react/jsx-runtime";
2
+ import t from "react";
3
+ const i = t.createContext({
4
+ mode: "sample",
5
+ isSample: !0,
6
+ isLive: !1,
7
+ toggle: () => {
8
+ },
9
+ setMode: () => {
10
+ }
11
+ }), l = "app-data-mode", c = ["sample", "live"];
12
+ function f() {
13
+ return t.useContext(i);
14
+ }
15
+ function v({ initialMode: n = "sample", children: r }) {
16
+ const [o, s] = t.useState(n);
17
+ t.useEffect(() => {
18
+ try {
19
+ const e = window.localStorage.getItem(l);
20
+ c.includes(e) && s(e);
21
+ } catch {
22
+ }
23
+ }, []), t.useEffect(() => {
24
+ try {
25
+ window.localStorage.setItem(l, o);
26
+ } catch {
27
+ }
28
+ }, [o]);
29
+ const a = t.useCallback((e) => {
30
+ c.includes(e) && s(e);
31
+ }, []), d = t.useMemo(
32
+ () => ({
33
+ mode: o,
34
+ isSample: o === "sample",
35
+ isLive: o === "live",
36
+ toggle: () => s((e) => e === "sample" ? "live" : "sample"),
37
+ setMode: a
38
+ }),
39
+ [o, a]
40
+ );
41
+ return /* @__PURE__ */ m(i.Provider, { value: d, children: r });
42
+ }
43
+ export {
44
+ v as default,
45
+ f as useDataMode
46
+ };
47
+ //# sourceMappingURL=DataModeProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataModeProvider.js","sources":["../../../../src/components/library/data/DataModeProvider.jsx"],"sourcesContent":["import React from \"react\";\n\nconst DataModeContext = React.createContext({\n mode: \"sample\",\n isSample: true,\n isLive: false,\n toggle: () => {},\n setMode: () => {},\n});\n\nconst STORAGE_KEY = \"app-data-mode\";\nconst VALID_MODES = [\"sample\", \"live\"];\n\n/**\n * Read the current data mode from any component.\n *\n * @returns {{ mode: \"sample\"|\"live\", isSample: boolean, isLive: boolean, toggle: () => void, setMode: (mode) => void }}\n */\nexport function useDataMode() {\n return React.useContext(DataModeContext);\n}\n\n/**\n * Provides global data-mode state (sample vs live) to the component tree.\n * Persists to localStorage so the choice survives page reloads.\n *\n * Wrap once in _app.js alongside AppThemeProvider.\n */\nexport default function DataModeProvider({ initialMode = \"sample\", children }) {\n const [mode, setModeState] = React.useState(initialMode);\n\n React.useEffect(() => {\n try {\n const stored = window.localStorage.getItem(STORAGE_KEY);\n if (VALID_MODES.includes(stored)) setModeState(stored);\n } catch {\n // SSR or storage unavailable\n }\n }, []);\n\n React.useEffect(() => {\n try {\n window.localStorage.setItem(STORAGE_KEY, mode);\n } catch {\n // ignore\n }\n }, [mode]);\n\n const setMode = React.useCallback((m) => {\n if (VALID_MODES.includes(m)) setModeState(m);\n }, []);\n\n const value = React.useMemo(\n () => ({\n mode,\n isSample: mode === \"sample\",\n isLive: mode === \"live\",\n toggle: () => setModeState((m) => (m === \"sample\" ? \"live\" : \"sample\")),\n setMode,\n }),\n [mode, setMode]\n );\n\n return (\n <DataModeContext.Provider value={value}>{children}</DataModeContext.Provider>\n );\n}\n"],"names":["DataModeContext","React","STORAGE_KEY","VALID_MODES","useDataMode","DataModeProvider","initialMode","children","mode","setModeState","stored","setMode","m","value","jsx"],"mappings":";;AAEA,MAAMA,IAAkBC,EAAM,cAAc;AAAA,EAC1C,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ,MAAM;AAAA,EAAC;AAAA,EACf,SAAS,MAAM;AAAA,EAAC;AAClB,CAAC,GAEKC,IAAc,iBACdC,IAAc,CAAC,UAAU,MAAM;AAO9B,SAASC,IAAc;AAC5B,SAAOH,EAAM,WAAWD,CAAe;AACzC;AAQA,SAAwBK,EAAiB,EAAE,aAAAC,IAAc,UAAU,UAAAC,KAAY;AAC7E,QAAM,CAACC,GAAMC,CAAY,IAAIR,EAAM,SAASK,CAAW;AAEvD,EAAAL,EAAM,UAAU,MAAM;AACpB,QAAI;AACF,YAAMS,IAAS,OAAO,aAAa,QAAQR,CAAW;AACtD,MAAIC,EAAY,SAASO,CAAM,OAAgBA,CAAM;AAAA,IACvD,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAA,CAAE,GAELT,EAAM,UAAU,MAAM;AACpB,QAAI;AACF,aAAO,aAAa,QAAQC,GAAaM,CAAI;AAAA,IAC/C,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAACA,CAAI,CAAC;AAET,QAAMG,IAAUV,EAAM,YAAY,CAACW,MAAM;AACvC,IAAIT,EAAY,SAASS,CAAC,OAAgBA,CAAC;AAAA,EAC7C,GAAG,CAAA,CAAE,GAECC,IAAQZ,EAAM;AAAA,IAClB,OAAO;AAAA,MACL,MAAAO;AAAA,MACA,UAAUA,MAAS;AAAA,MACnB,QAAQA,MAAS;AAAA,MACjB,QAAQ,MAAMC,EAAa,CAACG,MAAOA,MAAM,WAAW,SAAS,QAAS;AAAA,MACtE,SAAAD;AAAA,IAAA;AAAA,IAEF,CAACH,GAAMG,CAAO;AAAA,EAAA;AAGhB,SACE,gBAAAG,EAACd,EAAgB,UAAhB,EAAyB,OAAAa,GAAe,UAAAN,EAAA,CAAS;AAEtD;"}
@@ -0,0 +1,28 @@
1
+ import { jsxs as d, jsx as a } from "react/jsx-runtime";
2
+ import "react";
3
+ import { useDataMode as m } from "./DataModeProvider.js";
4
+ import { BeakerIcon as l, SignalIcon as i } from "@heroicons/react/24/outline";
5
+ function c({ className: o = "" }) {
6
+ const { mode: r, toggle: t } = m(), e = r === "sample";
7
+ return /* @__PURE__ */ d(
8
+ "button",
9
+ {
10
+ type: "button",
11
+ onClick: t,
12
+ className: [
13
+ "inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs font-semibold shadow-sm transition",
14
+ e ? "border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-300 dark:hover:bg-amber-950/60" : "border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-800 dark:bg-emerald-950/40 dark:text-emerald-300 dark:hover:bg-emerald-950/60",
15
+ o
16
+ ].filter(Boolean).join(" "),
17
+ "aria-label": `Data mode: ${r}. Click to switch to ${e ? "live" : "sample"}.`,
18
+ children: [
19
+ e ? /* @__PURE__ */ a(l, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ a(i, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
20
+ e ? "Sample" : "Live"
21
+ ]
22
+ }
23
+ );
24
+ }
25
+ export {
26
+ c as default
27
+ };
28
+ //# sourceMappingURL=DataModeToggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataModeToggle.js","sources":["../../../../src/components/library/data/DataModeToggle.jsx"],"sourcesContent":["import React from \"react\";\nimport { useDataMode } from \"./DataModeProvider\";\nimport { BeakerIcon, SignalIcon } from \"@heroicons/react/24/outline\";\n\n/**\n * Pill toggle for switching between sample and live data modes.\n * Place in the AppShell header next to the theme toggle.\n */\nexport default function DataModeToggle({ className = \"\" }) {\n const { mode, toggle } = useDataMode();\n const isSample = mode === \"sample\";\n\n return (\n <button\n type=\"button\"\n onClick={toggle}\n className={[\n \"inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs font-semibold shadow-sm transition\",\n isSample\n ? \"border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-300 dark:hover:bg-amber-950/60\"\n : \"border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-800 dark:bg-emerald-950/40 dark:text-emerald-300 dark:hover:bg-emerald-950/60\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")}\n aria-label={`Data mode: ${mode}. Click to switch to ${isSample ? \"live\" : \"sample\"}.`}\n >\n {isSample ? (\n <BeakerIcon className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n ) : (\n <SignalIcon className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n {isSample ? \"Sample\" : \"Live\"}\n </button>\n );\n}\n"],"names":["DataModeToggle","className","mode","toggle","useDataMode","isSample","jsxs","jsx","BeakerIcon","SignalIcon"],"mappings":";;;;AAQA,SAAwBA,EAAe,EAAE,WAAAC,IAAY,MAAM;AACzD,QAAM,EAAE,MAAAC,GAAM,QAAAC,EAAA,IAAWC,EAAA,GACnBC,IAAWH,MAAS;AAE1B,SACE,gBAAAI;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAASH;AAAA,MACT,WAAW;AAAA,QACT;AAAA,QACAE,IACI,6JACA;AAAA,QACJJ;AAAA,MAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MACX,cAAY,cAAcC,CAAI,wBAAwBG,IAAW,SAAS,QAAQ;AAAA,MAEjF,UAAA;AAAA,QAAAA,IACC,gBAAAE,EAACC,GAAA,EAAW,WAAU,eAAc,eAAY,OAAA,CAAO,IAEvD,gBAAAD,EAACE,GAAA,EAAW,WAAU,eAAc,eAAY,QAAO;AAAA,QAExDJ,IAAW,WAAW;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG7B;"}
@@ -0,0 +1,71 @@
1
+ function u(n, i, t = []) {
2
+ if (!i || !i.trim()) return n;
3
+ const e = i.trim().toLowerCase();
4
+ return n.filter(
5
+ (r) => t.some((o) => {
6
+ const l = r?.[o];
7
+ return l != null && String(l).toLowerCase().includes(e);
8
+ })
9
+ );
10
+ }
11
+ function f(n, i, t) {
12
+ return t == null || t === "" || t === "all" ? n : n.filter((e) => {
13
+ const r = e?.[i];
14
+ return typeof r == "string" && typeof t == "string" ? r.toLowerCase() === t.toLowerCase() : r === t;
15
+ });
16
+ }
17
+ function c(n, i, t, e) {
18
+ return t ? n.filter((r) => {
19
+ const o = r?.[i];
20
+ return e !== void 0 ? o === e : !!o;
21
+ }) : n;
22
+ }
23
+ function a(n, i, t) {
24
+ if (!t) return n;
25
+ const e = t.start ? new Date(t.start) : null, r = t.end ? new Date(t.end) : null;
26
+ return !e && !r ? n : n.filter((o) => {
27
+ const l = o?.[i];
28
+ if (l == null) return !1;
29
+ const s = l instanceof Date ? l : new Date(l);
30
+ return !(Number.isNaN(s.getTime()) || e && s < e || r && s > r);
31
+ });
32
+ }
33
+ function y(n, i, t = "asc") {
34
+ if (!i) return n;
35
+ const e = t === "desc" ? -1 : 1;
36
+ return [...n].sort((r, o) => {
37
+ const l = r?.[i], s = o?.[i];
38
+ return l == null && s == null ? 0 : l == null ? -1 * e : s == null ? 1 * e : typeof l == "number" && typeof s == "number" ? (l - s) * e : String(l).localeCompare(String(s)) * e;
39
+ });
40
+ }
41
+ function p(n, i = [], t = {}) {
42
+ let e = n;
43
+ for (const r of i) {
44
+ const o = t[r.id];
45
+ if (o != null)
46
+ switch (r.type) {
47
+ case "search":
48
+ e = u(e, o, r.keys ?? []);
49
+ break;
50
+ case "select":
51
+ e = f(e, r.key, o);
52
+ break;
53
+ case "toggle":
54
+ e = c(e, r.key, o, r.matchValue);
55
+ break;
56
+ case "dateRange":
57
+ e = a(e, r.key, o);
58
+ break;
59
+ }
60
+ }
61
+ return e;
62
+ }
63
+ export {
64
+ p as applyFilters,
65
+ a as filterByDateRange,
66
+ u as filterBySearch,
67
+ c as filterByToggle,
68
+ f as filterByValue,
69
+ y as sortByKey
70
+ };
71
+ //# sourceMappingURL=filterUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterUtils.js","sources":["../../../../src/components/library/data/filterUtils.jsx"],"sourcesContent":["/**\n * Pure data utilities for filtering, sorting, and searching.\n * Stateless — combine with usePageFilters hook for state management.\n */\n\n/**\n * Text search across multiple keys.\n * @param {Array} data\n * @param {string} query — search string\n * @param {string[]} keys — object keys to search within\n * @returns {Array} filtered data\n */\nexport function filterBySearch(data, query, keys = []) {\n if (!query || !query.trim()) return data;\n const q = query.trim().toLowerCase();\n return data.filter((row) =>\n keys.some((key) => {\n const val = row?.[key];\n return val != null && String(val).toLowerCase().includes(q);\n })\n );\n}\n\n/**\n * Filter rows where key matches a specific value.\n * Pass \"all\" or \"\" to skip filtering.\n * @param {Array} data\n * @param {string} key — object key to match\n * @param {*} value — value to match (exact, case-insensitive for strings)\n * @returns {Array}\n */\nexport function filterByValue(data, key, value) {\n if (value == null || value === \"\" || value === \"all\") return data;\n return data.filter((row) => {\n const v = row?.[key];\n if (typeof v === \"string\" && typeof value === \"string\") {\n return v.toLowerCase() === value.toLowerCase();\n }\n return v === value;\n });\n}\n\n/**\n * Filter rows where a boolean condition is met.\n * When toggle is off, returns all data (no filtering).\n * @param {Array} data\n * @param {string} key — object key to check\n * @param {boolean} isActive — whether the toggle is on\n * @param {*} matchValue — value that key should equal when active (default: truthy check)\n * @returns {Array}\n */\nexport function filterByToggle(data, key, isActive, matchValue) {\n if (!isActive) return data;\n return data.filter((row) => {\n const v = row?.[key];\n if (matchValue !== undefined) return v === matchValue;\n return Boolean(v);\n });\n}\n\n/**\n * Filter rows where a date field falls within a range.\n * @param {Array} data\n * @param {string} key — object key containing date (ISO string or Date)\n * @param {{ start?: Date|string, end?: Date|string }} range\n * @returns {Array}\n */\nexport function filterByDateRange(data, key, range) {\n if (!range) return data;\n const start = range.start ? new Date(range.start) : null;\n const end = range.end ? new Date(range.end) : null;\n if (!start && !end) return data;\n\n return data.filter((row) => {\n const raw = row?.[key];\n if (raw == null) return false;\n const d = raw instanceof Date ? raw : new Date(raw);\n if (Number.isNaN(d.getTime())) return false;\n if (start && d < start) return false;\n if (end && d > end) return false;\n return true;\n });\n}\n\n/**\n * Sort data by a key.\n * @param {Array} data\n * @param {string} key — object key to sort by\n * @param {\"asc\"|\"desc\"} direction\n * @returns {Array} new sorted array\n */\nexport function sortByKey(data, key, direction = \"asc\") {\n if (!key) return data;\n const dir = direction === \"desc\" ? -1 : 1;\n return [...data].sort((a, b) => {\n const av = a?.[key];\n const bv = b?.[key];\n if (av == null && bv == null) return 0;\n if (av == null) return -1 * dir;\n if (bv == null) return 1 * dir;\n if (typeof av === \"number\" && typeof bv === \"number\") return (av - bv) * dir;\n return String(av).localeCompare(String(bv)) * dir;\n });\n}\n\n/**\n * Apply a set of filter definitions to data.\n * Each filter in `filters` has { id, type, key/keys } and `values` holds the current state.\n *\n * @param {Array} data\n * @param {Array} filters — filter definitions [{ id, type, key?, keys? }]\n * @param {Object} values — current filter values keyed by filter id\n * @returns {Array} filtered data\n */\nexport function applyFilters(data, filters = [], values = {}) {\n let result = data;\n\n for (const filter of filters) {\n const val = values[filter.id];\n if (val === undefined || val === null) continue;\n\n switch (filter.type) {\n case \"search\":\n result = filterBySearch(result, val, filter.keys ?? []);\n break;\n case \"select\":\n result = filterByValue(result, filter.key, val);\n break;\n case \"toggle\":\n result = filterByToggle(result, filter.key, val, filter.matchValue);\n break;\n case \"dateRange\":\n result = filterByDateRange(result, filter.key, val);\n break;\n default:\n break;\n }\n }\n\n return result;\n}\n"],"names":["filterBySearch","data","query","keys","q","row","key","val","filterByValue","value","v","filterByToggle","isActive","matchValue","filterByDateRange","range","start","end","raw","d","sortByKey","direction","dir","a","b","av","bv","applyFilters","filters","values","result","filter"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { useMemo as u } from "react";
2
+ import { useDataMode as n } from "./DataModeProvider.js";
3
+ function c({ sample: t, live: e }) {
4
+ const { mode: r } = n();
5
+ return u(() => {
6
+ const o = r === "sample" ? t : e;
7
+ return typeof o == "function" ? o() : o;
8
+ }, [r, t, e]);
9
+ }
10
+ export {
11
+ c as default
12
+ };
13
+ //# sourceMappingURL=useDataSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDataSource.js","sources":["../../../../src/components/library/data/useDataSource.jsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useDataMode } from \"./DataModeProvider\";\n\n/**\n * Select between sample and live data based on the global data mode.\n *\n * Values can be plain data or functions (lazy-evaluated only when active).\n *\n * @param {{ sample: any | () => any, live: any | () => any }} sources\n * @returns {any} the resolved value for the active mode\n *\n * @example\n * // Static data\n * const incidents = useDataSource({\n * sample: sampleIncidents,\n * live: fetchedIncidents,\n * });\n *\n * @example\n * // Lazy — factory only runs when that mode is active\n * const metrics = useDataSource({\n * sample: () => generateSampleMetrics(),\n * live: () => computeFromAPI(apiData),\n * });\n */\nexport default function useDataSource({ sample, live }) {\n const { mode } = useDataMode();\n\n return useMemo(() => {\n const source = mode === \"sample\" ? sample : live;\n return typeof source === \"function\" ? source() : source;\n }, [mode, sample, live]);\n}\n"],"names":["useDataSource","sample","live","mode","useDataMode","useMemo","source"],"mappings":";;AAyBA,SAAwBA,EAAc,EAAE,QAAAC,GAAQ,MAAAC,KAAQ;AACtD,QAAM,EAAE,MAAAC,EAAA,IAASC,EAAA;AAEjB,SAAOC,EAAQ,MAAM;AACnB,UAAMC,IAASH,MAAS,WAAWF,IAASC;AAC5C,WAAO,OAAOI,KAAW,aAAaA,EAAA,IAAWA;AAAA,EACnD,GAAG,CAACH,GAAMF,GAAQC,CAAI,CAAC;AACzB;"}
@@ -0,0 +1,46 @@
1
+ import { useMemo as r, useState as p, useCallback as a } from "react";
2
+ import { applyFilters as h, sortByKey as C } from "./filterUtils.js";
3
+ function b({ data: u = [], filters: o = [], defaultSort: y = null } = {}) {
4
+ const c = r(() => {
5
+ const t = {};
6
+ for (const e of o)
7
+ e.defaultValue !== void 0 ? t[e.id] = e.defaultValue : e.type === "search" ? t[e.id] = "" : e.type === "select" ? t[e.id] = "all" : e.type === "toggle" ? t[e.id] = !1 : e.type === "dateRange" && (t[e.id] = null);
8
+ return t;
9
+ }, [o]), [l, f] = p(c), [i, d] = p(y), g = a((t, e) => {
10
+ f((s) => ({ ...s, [t]: e }));
11
+ }, []), m = a(() => {
12
+ f(c);
13
+ }, [c]), F = a((t, e) => {
14
+ d(t ? { key: t, direction: e ?? "asc" } : null);
15
+ }, []), S = a((t) => {
16
+ d((e) => e?.key !== t ? { key: t, direction: "asc" } : e.direction === "asc" ? { key: t, direction: "desc" } : null);
17
+ }, []), n = r(
18
+ () => h(u, o, l),
19
+ [u, o, l]
20
+ ), v = r(
21
+ () => i ? C(n, i.key, i.direction) : n,
22
+ [n, i]
23
+ ), V = r(() => {
24
+ let t = 0;
25
+ for (const e of o) {
26
+ const s = l[e.id];
27
+ (e.type === "search" && s && s.trim() || e.type === "select" && s && s !== "all" || e.type === "toggle" && s || e.type === "dateRange" && s) && t++;
28
+ }
29
+ return t;
30
+ }, [o, l]);
31
+ return {
32
+ values: l,
33
+ setFilter: g,
34
+ resetFilters: m,
35
+ sort: i,
36
+ setSort: F,
37
+ toggleSort: S,
38
+ filteredData: n,
39
+ sortedData: v,
40
+ activeFilterCount: V
41
+ };
42
+ }
43
+ export {
44
+ b as default
45
+ };
46
+ //# sourceMappingURL=usePageFilters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePageFilters.js","sources":["../../../../src/components/library/data/usePageFilters.jsx"],"sourcesContent":["import { useState, useMemo, useCallback } from \"react\";\nimport { applyFilters, sortByKey } from \"./filterUtils\";\n\n/**\n * Hook for managing page-level filter and sort state.\n *\n * @param {Object} options\n * @param {Array} options.data — raw data array\n * @param {Array} options.filters — filter definitions [{ id, type, key?, keys?, defaultValue? }]\n * @param {Object} options.defaultSort — { key, direction } or null\n * @returns {Object} { values, setFilter, resetFilters, sort, setSort, filteredData, sortedData, activeFilterCount }\n *\n * @example\n * const { values, setFilter, resetFilters, sortedData } = usePageFilters({\n * data: incidents,\n * filters: [\n * { id: \"search\", type: \"search\", keys: [\"title\", \"description\"] },\n * { id: \"severity\", type: \"select\", key: \"severity\", defaultValue: \"all\" },\n * { id: \"active\", type: \"toggle\", key: \"resolved\", matchValue: false },\n * ],\n * defaultSort: { key: \"timestamp\", direction: \"desc\" },\n * });\n */\nexport default function usePageFilters({ data = [], filters = [], defaultSort = null } = {}) {\n const initialValues = useMemo(() => {\n const v = {};\n for (const f of filters) {\n if (f.defaultValue !== undefined) {\n v[f.id] = f.defaultValue;\n } else if (f.type === \"search\") {\n v[f.id] = \"\";\n } else if (f.type === \"select\") {\n v[f.id] = \"all\";\n } else if (f.type === \"toggle\") {\n v[f.id] = false;\n } else if (f.type === \"dateRange\") {\n v[f.id] = null;\n }\n }\n return v;\n }, [filters]);\n\n const [values, setValues] = useState(initialValues);\n const [sort, setSortState] = useState(defaultSort);\n\n const setFilter = useCallback((id, value) => {\n setValues((prev) => ({ ...prev, [id]: value }));\n }, []);\n\n const resetFilters = useCallback(() => {\n setValues(initialValues);\n }, [initialValues]);\n\n const setSort = useCallback((key, direction) => {\n setSortState(key ? { key, direction: direction ?? \"asc\" } : null);\n }, []);\n\n const toggleSort = useCallback((key) => {\n setSortState((prev) => {\n if (prev?.key !== key) return { key, direction: \"asc\" };\n if (prev.direction === \"asc\") return { key, direction: \"desc\" };\n return null;\n });\n }, []);\n\n const filteredData = useMemo(\n () => applyFilters(data, filters, values),\n [data, filters, values]\n );\n\n const sortedData = useMemo(\n () => (sort ? sortByKey(filteredData, sort.key, sort.direction) : filteredData),\n [filteredData, sort]\n );\n\n const activeFilterCount = useMemo(() => {\n let count = 0;\n for (const f of filters) {\n const v = values[f.id];\n if (f.type === \"search\" && v && v.trim()) count++;\n else if (f.type === \"select\" && v && v !== \"all\") count++;\n else if (f.type === \"toggle\" && v) count++;\n else if (f.type === \"dateRange\" && v) count++;\n }\n return count;\n }, [filters, values]);\n\n return {\n values,\n setFilter,\n resetFilters,\n sort,\n setSort,\n toggleSort,\n filteredData,\n sortedData,\n activeFilterCount,\n };\n}\n"],"names":["usePageFilters","data","filters","defaultSort","initialValues","useMemo","v","f","values","setValues","useState","sort","setSortState","setFilter","useCallback","id","value","prev","resetFilters","setSort","key","direction","toggleSort","filteredData","applyFilters","sortedData","sortByKey","activeFilterCount","count"],"mappings":";;AAuBA,SAAwBA,EAAe,EAAE,MAAAC,IAAO,IAAI,SAAAC,IAAU,CAAA,GAAI,aAAAC,IAAc,KAAA,IAAS,IAAI;AAC3F,QAAMC,IAAgBC,EAAQ,MAAM;AAClC,UAAMC,IAAI,CAAA;AACV,eAAWC,KAAKL;AACd,MAAIK,EAAE,iBAAiB,SACrBD,EAAEC,EAAE,EAAE,IAAIA,EAAE,eACHA,EAAE,SAAS,WACpBD,EAAEC,EAAE,EAAE,IAAI,KACDA,EAAE,SAAS,WACpBD,EAAEC,EAAE,EAAE,IAAI,QACDA,EAAE,SAAS,WACpBD,EAAEC,EAAE,EAAE,IAAI,KACDA,EAAE,SAAS,gBACpBD,EAAEC,EAAE,EAAE,IAAI;AAGd,WAAOD;AAAA,EACT,GAAG,CAACJ,CAAO,CAAC,GAEN,CAACM,GAAQC,CAAS,IAAIC,EAASN,CAAa,GAC5C,CAACO,GAAMC,CAAY,IAAIF,EAASP,CAAW,GAE3CU,IAAYC,EAAY,CAACC,GAAIC,MAAU;AAC3C,IAAAP,EAAU,CAACQ,OAAU,EAAE,GAAGA,GAAM,CAACF,CAAE,GAAGC,EAAA,EAAQ;AAAA,EAChD,GAAG,CAAA,CAAE,GAECE,IAAeJ,EAAY,MAAM;AACrC,IAAAL,EAAUL,CAAa;AAAA,EACzB,GAAG,CAACA,CAAa,CAAC,GAEZe,IAAUL,EAAY,CAACM,GAAKC,MAAc;AAC9C,IAAAT,EAAaQ,IAAM,EAAE,KAAAA,GAAK,WAAWC,KAAa,MAAA,IAAU,IAAI;AAAA,EAClE,GAAG,CAAA,CAAE,GAECC,IAAaR,EAAY,CAACM,MAAQ;AACtC,IAAAR,EAAa,CAACK,MACRA,GAAM,QAAQG,IAAY,EAAE,KAAAA,GAAK,WAAW,MAAA,IAC5CH,EAAK,cAAc,QAAc,EAAE,KAAAG,GAAK,WAAW,OAAA,IAChD,IACR;AAAA,EACH,GAAG,CAAA,CAAE,GAECG,IAAelB;AAAA,IACnB,MAAMmB,EAAavB,GAAMC,GAASM,CAAM;AAAA,IACxC,CAACP,GAAMC,GAASM,CAAM;AAAA,EAAA,GAGlBiB,IAAapB;AAAA,IACjB,MAAOM,IAAOe,EAAUH,GAAcZ,EAAK,KAAKA,EAAK,SAAS,IAAIY;AAAA,IAClE,CAACA,GAAcZ,CAAI;AAAA,EAAA,GAGfgB,IAAoBtB,EAAQ,MAAM;AACtC,QAAIuB,IAAQ;AACZ,eAAWrB,KAAKL,GAAS;AACvB,YAAMI,IAAIE,EAAOD,EAAE,EAAE;AACrB,OAAIA,EAAE,SAAS,YAAYD,KAAKA,EAAE,UACzBC,EAAE,SAAS,YAAYD,KAAKA,MAAM,SAClCC,EAAE,SAAS,YAAYD,KACvBC,EAAE,SAAS,eAAeD,MAAGsB;AAAA,IACxC;AACA,WAAOA;AAAA,EACT,GAAG,CAAC1B,GAASM,CAAM,CAAC;AAEpB,SAAO;AAAA,IACL,QAAAA;AAAA,IACA,WAAAK;AAAA,IACA,cAAAK;AAAA,IACA,MAAAP;AAAA,IACA,SAAAQ;AAAA,IACA,YAAAG;AAAA,IACA,cAAAC;AAAA,IACA,YAAAE;AAAA,IACA,mBAAAE;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,89 @@
1
+ import { jsxs as m, jsx as a } from "react/jsx-runtime";
2
+ import "react";
3
+ import p from "./SearchFilter.js";
4
+ import u from "./SelectFilter.js";
5
+ import h from "./ToggleFilter.js";
6
+ import { XMarkIcon as x } from "@heroicons/react/24/outline";
7
+ function f({
8
+ filters: o = [],
9
+ values: d = {},
10
+ onChange: s,
11
+ onReset: c,
12
+ activeCount: t = 0,
13
+ layout: i = "inline"
14
+ }) {
15
+ if (!o.length) return null;
16
+ const n = i === "stacked";
17
+ return /* @__PURE__ */ m(
18
+ "div",
19
+ {
20
+ className: [
21
+ "flex gap-3",
22
+ n ? "flex-col" : "flex-col sm:flex-row sm:flex-wrap sm:items-center"
23
+ ].join(" "),
24
+ children: [
25
+ o.map((e) => {
26
+ const r = d[e.id];
27
+ switch (e.type) {
28
+ case "search":
29
+ return /* @__PURE__ */ a(
30
+ p,
31
+ {
32
+ value: r ?? "",
33
+ onChange: (l) => s?.(e.id, l),
34
+ placeholder: e.placeholder ?? "Search…",
35
+ className: e.className ?? (n ? "w-full" : "w-full sm:w-64")
36
+ },
37
+ e.id
38
+ );
39
+ case "select":
40
+ return /* @__PURE__ */ a(
41
+ u,
42
+ {
43
+ value: r ?? "all",
44
+ onChange: (l) => s?.(e.id, l),
45
+ options: e.options ?? [],
46
+ label: e.label,
47
+ placeholder: e.placeholder ?? "All",
48
+ className: e.className
49
+ },
50
+ e.id
51
+ );
52
+ case "toggle":
53
+ return /* @__PURE__ */ a(
54
+ h,
55
+ {
56
+ value: r ?? !1,
57
+ onChange: (l) => s?.(e.id, l),
58
+ label: e.label,
59
+ className: e.className
60
+ },
61
+ e.id
62
+ );
63
+ default:
64
+ return null;
65
+ }
66
+ }),
67
+ t > 0 && c ? /* @__PURE__ */ m(
68
+ "button",
69
+ {
70
+ type: "button",
71
+ onClick: c,
72
+ className: "inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium text-slate-500 transition hover:bg-slate-100 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200",
73
+ children: [
74
+ /* @__PURE__ */ a(x, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
75
+ "Clear ",
76
+ t,
77
+ " ",
78
+ t === 1 ? "filter" : "filters"
79
+ ]
80
+ }
81
+ ) : null
82
+ ]
83
+ }
84
+ );
85
+ }
86
+ export {
87
+ f as default
88
+ };
89
+ //# sourceMappingURL=FilterBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterBar.js","sources":["../../../../src/components/library/filters/FilterBar.jsx"],"sourcesContent":["import React from \"react\";\nimport SearchFilter from \"./SearchFilter\";\nimport SelectFilter from \"./SelectFilter\";\nimport ToggleFilter from \"./ToggleFilter\";\nimport { FunnelIcon, XMarkIcon } from \"@heroicons/react/24/outline\";\n\n/**\n * Renders a row of filter controls from a definitions array.\n * Pairs with usePageFilters hook for state management.\n *\n * @param {Array} filters — filter definitions [{ id, type, ... }]\n * @param {Object} values — current filter values keyed by filter id\n * @param {Function} onChange — (filterId, value) => void\n * @param {Function} onReset — () => void\n * @param {number} activeCount — number of active filters (for badge)\n * @param {string} layout — \"inline\" (default) or \"stacked\"\n */\nexport default function FilterBar({\n filters = [],\n values = {},\n onChange,\n onReset,\n activeCount = 0,\n layout = \"inline\",\n}) {\n if (!filters.length) return null;\n\n const isStacked = layout === \"stacked\";\n\n return (\n <div\n className={[\n \"flex gap-3\",\n isStacked\n ? \"flex-col\"\n : \"flex-col sm:flex-row sm:flex-wrap sm:items-center\",\n ].join(\" \")}\n >\n {filters.map((filter) => {\n const val = values[filter.id];\n\n switch (filter.type) {\n case \"search\":\n return (\n <SearchFilter\n key={filter.id}\n value={val ?? \"\"}\n onChange={(v) => onChange?.(filter.id, v)}\n placeholder={filter.placeholder ?? \"Search…\"}\n className={filter.className ?? (isStacked ? \"w-full\" : \"w-full sm:w-64\")}\n />\n );\n\n case \"select\":\n return (\n <SelectFilter\n key={filter.id}\n value={val ?? \"all\"}\n onChange={(v) => onChange?.(filter.id, v)}\n options={filter.options ?? []}\n label={filter.label}\n placeholder={filter.placeholder ?? \"All\"}\n className={filter.className}\n />\n );\n\n case \"toggle\":\n return (\n <ToggleFilter\n key={filter.id}\n value={val ?? false}\n onChange={(v) => onChange?.(filter.id, v)}\n label={filter.label}\n className={filter.className}\n />\n );\n\n default:\n return null;\n }\n })}\n\n {activeCount > 0 && onReset ? (\n <button\n type=\"button\"\n onClick={onReset}\n className=\"inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium text-slate-500 transition hover:bg-slate-100 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200\"\n >\n <XMarkIcon className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Clear {activeCount} {activeCount === 1 ? \"filter\" : \"filters\"}\n </button>\n ) : null}\n </div>\n );\n}\n"],"names":["FilterBar","filters","values","onChange","onReset","activeCount","layout","isStacked","jsxs","filter","val","jsx","SearchFilter","v","SelectFilter","ToggleFilter","XMarkIcon"],"mappings":";;;;;;AAiBA,SAAwBA,EAAU;AAAA,EAChC,SAAAC,IAAU,CAAA;AAAA,EACV,QAAAC,IAAS,CAAA;AAAA,EACT,UAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,QAAAC,IAAS;AACX,GAAG;AACD,MAAI,CAACL,EAAQ,OAAQ,QAAO;AAE5B,QAAMM,IAAYD,MAAW;AAE7B,SACE,gBAAAE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACAD,IACI,aACA;AAAA,MAAA,EACJ,KAAK,GAAG;AAAA,MAET,UAAA;AAAA,QAAAN,EAAQ,IAAI,CAACQ,MAAW;AACvB,gBAAMC,IAAMR,EAAOO,EAAO,EAAE;AAE5B,kBAAQA,EAAO,MAAA;AAAA,YACb,KAAK;AACH,qBACE,gBAAAE;AAAA,gBAACC;AAAA,gBAAA;AAAA,kBAEC,OAAOF,KAAO;AAAA,kBACd,UAAU,CAACG,MAAMV,IAAWM,EAAO,IAAII,CAAC;AAAA,kBACxC,aAAaJ,EAAO,eAAe;AAAA,kBACnC,WAAWA,EAAO,cAAcF,IAAY,WAAW;AAAA,gBAAA;AAAA,gBAJlDE,EAAO;AAAA,cAAA;AAAA,YAQlB,KAAK;AACH,qBACE,gBAAAE;AAAA,gBAACG;AAAA,gBAAA;AAAA,kBAEC,OAAOJ,KAAO;AAAA,kBACd,UAAU,CAACG,MAAMV,IAAWM,EAAO,IAAII,CAAC;AAAA,kBACxC,SAASJ,EAAO,WAAW,CAAA;AAAA,kBAC3B,OAAOA,EAAO;AAAA,kBACd,aAAaA,EAAO,eAAe;AAAA,kBACnC,WAAWA,EAAO;AAAA,gBAAA;AAAA,gBANbA,EAAO;AAAA,cAAA;AAAA,YAUlB,KAAK;AACH,qBACE,gBAAAE;AAAA,gBAACI;AAAA,gBAAA;AAAA,kBAEC,OAAOL,KAAO;AAAA,kBACd,UAAU,CAACG,MAAMV,IAAWM,EAAO,IAAII,CAAC;AAAA,kBACxC,OAAOJ,EAAO;AAAA,kBACd,WAAWA,EAAO;AAAA,gBAAA;AAAA,gBAJbA,EAAO;AAAA,cAAA;AAAA,YAQlB;AACE,qBAAO;AAAA,UAAA;AAAA,QAEb,CAAC;AAAA,QAEAJ,IAAc,KAAKD,IAClB,gBAAAI;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAASJ;AAAA,YACT,WAAU;AAAA,YAEV,UAAA;AAAA,cAAA,gBAAAO,EAACK,GAAA,EAAU,WAAU,eAAc,eAAY,QAAO;AAAA,cAAE;AAAA,cACjDX;AAAA,cAAY;AAAA,cAAEA,MAAgB,IAAI,WAAW;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,IAEpD;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV;"}
@@ -0,0 +1,44 @@
1
+ import { jsxs as o, jsx as e } from "react/jsx-runtime";
2
+ import "react";
3
+ import { MagnifyingGlassIcon as n, XMarkIcon as i } from "@heroicons/react/24/outline";
4
+ function f({
5
+ value: t = "",
6
+ onChange: a,
7
+ placeholder: r = "Search…",
8
+ className: l = ""
9
+ }) {
10
+ return /* @__PURE__ */ o("div", { className: ["relative", l].filter(Boolean).join(" "), children: [
11
+ /* @__PURE__ */ e(
12
+ n,
13
+ {
14
+ className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500",
15
+ "aria-hidden": "true"
16
+ }
17
+ ),
18
+ /* @__PURE__ */ e(
19
+ "input",
20
+ {
21
+ type: "text",
22
+ value: t,
23
+ onChange: (s) => a?.(s.target.value),
24
+ placeholder: r,
25
+ className: "h-9 w-full rounded-lg border border-slate-200 bg-white pl-9 pr-8 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500 dark:focus:ring-offset-slate-950",
26
+ "aria-label": r
27
+ }
28
+ ),
29
+ t ? /* @__PURE__ */ e(
30
+ "button",
31
+ {
32
+ type: "button",
33
+ onClick: () => a?.(""),
34
+ className: "absolute right-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300",
35
+ "aria-label": "Clear search",
36
+ children: /* @__PURE__ */ e(i, { className: "h-4 w-4" })
37
+ }
38
+ ) : null
39
+ ] });
40
+ }
41
+ export {
42
+ f as default
43
+ };
44
+ //# sourceMappingURL=SearchFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchFilter.js","sources":["../../../../src/components/library/filters/SearchFilter.jsx"],"sourcesContent":["import React from \"react\";\nimport { MagnifyingGlassIcon, XMarkIcon } from \"@heroicons/react/24/outline\";\n\nexport default function SearchFilter({\n value = \"\",\n onChange,\n placeholder = \"Search…\",\n className = \"\",\n}) {\n return (\n <div className={[\"relative\", className].filter(Boolean).join(\" \")}>\n <MagnifyingGlassIcon\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500\"\n aria-hidden=\"true\"\n />\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange?.(e.target.value)}\n placeholder={placeholder}\n className=\"h-9 w-full rounded-lg border border-slate-200 bg-white pl-9 pr-8 text-sm text-slate-900 shadow-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500 dark:focus:ring-offset-slate-950\"\n aria-label={placeholder}\n />\n {value ? (\n <button\n type=\"button\"\n onClick={() => onChange?.(\"\")}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300\"\n aria-label=\"Clear search\"\n >\n <XMarkIcon className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n );\n}\n"],"names":["SearchFilter","value","onChange","placeholder","className","jsxs","jsx","MagnifyingGlassIcon","e","XMarkIcon"],"mappings":";;;AAGA,SAAwBA,EAAa;AAAA,EACnC,OAAAC,IAAQ;AAAA,EACR,UAAAC;AAAA,EACA,aAAAC,IAAc;AAAA,EACd,WAAAC,IAAY;AACd,GAAG;AACD,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAW,CAAC,YAAYD,CAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAC9D,UAAA;AAAA,IAAA,gBAAAE;AAAA,MAACC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,eAAY;AAAA,MAAA;AAAA,IAAA;AAAA,IAEd,gBAAAD;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,OAAAL;AAAA,QACA,UAAU,CAACO,MAAMN,IAAWM,EAAE,OAAO,KAAK;AAAA,QAC1C,aAAAL;AAAA,QACA,WAAU;AAAA,QACV,cAAYA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEbF,IACC,gBAAAK;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAMJ,IAAW,EAAE;AAAA,QAC5B,WAAU;AAAA,QACV,cAAW;AAAA,QAEX,UAAA,gBAAAI,EAACG,GAAA,EAAU,WAAU,UAAA,CAAU;AAAA,MAAA;AAAA,IAAA,IAE/B;AAAA,EAAA,GACN;AAEJ;"}
@@ -0,0 +1,44 @@
1
+ import { jsxs as n, jsx as t } from "react/jsx-runtime";
2
+ import "react";
3
+ import { ChevronDownIcon as u } from "@heroicons/react/24/outline";
4
+ function h({
5
+ value: r = "all",
6
+ onChange: s,
7
+ options: i = [],
8
+ label: a,
9
+ placeholder: l,
10
+ className: o = ""
11
+ }) {
12
+ const d = i.map(
13
+ (e) => typeof e == "string" ? { value: e, label: e } : e
14
+ );
15
+ return /* @__PURE__ */ n("div", { className: ["relative inline-flex items-center gap-2", o].filter(Boolean).join(" "), children: [
16
+ a ? /* @__PURE__ */ t("span", { className: "shrink-0 text-xs font-medium text-slate-500 dark:text-slate-400", children: a }) : null,
17
+ /* @__PURE__ */ n("div", { className: "relative", children: [
18
+ /* @__PURE__ */ n(
19
+ "select",
20
+ {
21
+ value: r,
22
+ onChange: (e) => s?.(e.target.value),
23
+ className: "h-9 appearance-none rounded-lg border border-slate-200 bg-white py-0 pl-3 pr-8 text-sm font-medium text-slate-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-200 dark:focus:ring-offset-slate-950",
24
+ "aria-label": a ?? l ?? "Filter",
25
+ children: [
26
+ l ? /* @__PURE__ */ t("option", { value: "all", children: l }) : null,
27
+ d.map((e) => /* @__PURE__ */ t("option", { value: e.value, children: e.label }, e.value))
28
+ ]
29
+ }
30
+ ),
31
+ /* @__PURE__ */ t(
32
+ u,
33
+ {
34
+ className: "pointer-events-none absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500",
35
+ "aria-hidden": "true"
36
+ }
37
+ )
38
+ ] })
39
+ ] });
40
+ }
41
+ export {
42
+ h as default
43
+ };
44
+ //# sourceMappingURL=SelectFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectFilter.js","sources":["../../../../src/components/library/filters/SelectFilter.jsx"],"sourcesContent":["import React from \"react\";\nimport { ChevronDownIcon } from \"@heroicons/react/24/outline\";\n\n/**\n * Dropdown select filter.\n *\n * @param {string} value — current selected value\n * @param {Function} onChange — (value) => void\n * @param {Array} options — [{ value, label }] or [\"string\", ...]\n * @param {string} label — visible label\n * @param {string} placeholder — placeholder when no value selected\n */\nexport default function SelectFilter({\n value = \"all\",\n onChange,\n options = [],\n label,\n placeholder,\n className = \"\",\n}) {\n const normalizedOptions = options.map((opt) =>\n typeof opt === \"string\" ? { value: opt, label: opt } : opt\n );\n\n return (\n <div className={[\"relative inline-flex items-center gap-2\", className].filter(Boolean).join(\" \")}>\n {label ? (\n <span className=\"shrink-0 text-xs font-medium text-slate-500 dark:text-slate-400\">\n {label}\n </span>\n ) : null}\n <div className=\"relative\">\n <select\n value={value}\n onChange={(e) => onChange?.(e.target.value)}\n className=\"h-9 appearance-none rounded-lg border border-slate-200 bg-white py-0 pl-3 pr-8 text-sm font-medium text-slate-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-200 dark:focus:ring-offset-slate-950\"\n aria-label={label ?? placeholder ?? \"Filter\"}\n >\n {placeholder ? (\n <option value=\"all\">{placeholder}</option>\n ) : null}\n {normalizedOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <ChevronDownIcon\n className=\"pointer-events-none absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500\"\n aria-hidden=\"true\"\n />\n </div>\n </div>\n );\n}\n"],"names":["SelectFilter","value","onChange","options","label","placeholder","className","normalizedOptions","opt","jsxs","jsx","ChevronDownIcon"],"mappings":";;;AAYA,SAAwBA,EAAa;AAAA,EACnC,OAAAC,IAAQ;AAAA,EACR,UAAAC;AAAA,EACA,SAAAC,IAAU,CAAA;AAAA,EACV,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC,IAAY;AACd,GAAG;AACD,QAAMC,IAAoBJ,EAAQ;AAAA,IAAI,CAACK,MACrC,OAAOA,KAAQ,WAAW,EAAE,OAAOA,GAAK,OAAOA,MAAQA;AAAA,EAAA;AAGzD,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAW,CAAC,2CAA2CH,CAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAC5F,UAAA;AAAA,IAAAF,IACC,gBAAAM,EAAC,QAAA,EAAK,WAAU,mEACb,aACH,IACE;AAAA,IACJ,gBAAAD,EAAC,OAAA,EAAI,WAAU,YACb,UAAA;AAAA,MAAA,gBAAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAAR;AAAA,UACA,UAAU,CAAC,MAAMC,IAAW,EAAE,OAAO,KAAK;AAAA,UAC1C,WAAU;AAAA,UACV,cAAYE,KAASC,KAAe;AAAA,UAEnC,UAAA;AAAA,YAAAA,IACC,gBAAAK,EAAC,UAAA,EAAO,OAAM,OAAO,aAAY,IAC/B;AAAA,YACHH,EAAkB,IAAI,CAACC,MACtB,gBAAAE,EAAC,UAAA,EAAuB,OAAOF,EAAI,OAChC,UAAAA,EAAI,MAAA,GADMA,EAAI,KAEjB,CACD;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAEH,gBAAAE;AAAA,QAACC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,eAAY;AAAA,QAAA;AAAA,MAAA;AAAA,IACd,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
@@ -0,0 +1,48 @@
1
+ import { jsxs as o, jsx as n } from "react/jsx-runtime";
2
+ import "react";
3
+ function l({
4
+ value: e = !1,
5
+ onChange: r,
6
+ label: t,
7
+ className: s = ""
8
+ }) {
9
+ return /* @__PURE__ */ o(
10
+ "label",
11
+ {
12
+ className: [
13
+ "inline-flex cursor-pointer items-center gap-2",
14
+ s
15
+ ].filter(Boolean).join(" "),
16
+ children: [
17
+ /* @__PURE__ */ n(
18
+ "button",
19
+ {
20
+ type: "button",
21
+ role: "switch",
22
+ "aria-checked": e,
23
+ onClick: () => r?.(!e),
24
+ className: [
25
+ "relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-slate-950",
26
+ e ? "bg-brand-500" : "bg-slate-200 dark:bg-slate-700"
27
+ ].join(" "),
28
+ children: /* @__PURE__ */ n(
29
+ "span",
30
+ {
31
+ "aria-hidden": "true",
32
+ className: [
33
+ "pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow ring-0 transition-transform",
34
+ e ? "translate-x-4" : "translate-x-0"
35
+ ].join(" ")
36
+ }
37
+ )
38
+ }
39
+ ),
40
+ t ? /* @__PURE__ */ n("span", { className: "text-sm font-medium text-slate-700 dark:text-slate-200", children: t }) : null
41
+ ]
42
+ }
43
+ );
44
+ }
45
+ export {
46
+ l as default
47
+ };
48
+ //# sourceMappingURL=ToggleFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToggleFilter.js","sources":["../../../../src/components/library/filters/ToggleFilter.jsx"],"sourcesContent":["import React from \"react\";\n\n/**\n * Toggle switch filter.\n *\n * @param {boolean} value — current on/off state\n * @param {Function} onChange — (boolean) => void\n * @param {string} label — visible label\n */\nexport default function ToggleFilter({\n value = false,\n onChange,\n label,\n className = \"\",\n}) {\n return (\n <label\n className={[\n \"inline-flex cursor-pointer items-center gap-2\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={value}\n onClick={() => onChange?.(!value)}\n className={[\n \"relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-slate-950\",\n value\n ? \"bg-brand-500\"\n : \"bg-slate-200 dark:bg-slate-700\",\n ].join(\" \")}\n >\n <span\n aria-hidden=\"true\"\n className={[\n \"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow ring-0 transition-transform\",\n value ? \"translate-x-4\" : \"translate-x-0\",\n ].join(\" \")}\n />\n </button>\n {label ? (\n <span className=\"text-sm font-medium text-slate-700 dark:text-slate-200\">\n {label}\n </span>\n ) : null}\n </label>\n );\n}\n"],"names":["ToggleFilter","value","onChange","label","className","jsxs","jsx"],"mappings":";;AASA,SAAwBA,EAAa;AAAA,EACnC,OAAAC,IAAQ;AAAA,EACR,UAAAC;AAAA,EACA,OAAAC;AAAA,EACA,WAAAC,IAAY;AACd,GAAG;AACD,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACAD;AAAA,MAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX,UAAA;AAAA,QAAA,gBAAAE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,gBAAcL;AAAA,YACd,SAAS,MAAMC,IAAW,CAACD,CAAK;AAAA,YAChC,WAAW;AAAA,cACT;AAAA,cACAA,IACI,iBACA;AAAA,YAAA,EACJ,KAAK,GAAG;AAAA,YAEV,UAAA,gBAAAK;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,eAAY;AAAA,gBACZ,WAAW;AAAA,kBACT;AAAA,kBACAL,IAAQ,kBAAkB;AAAA,gBAAA,EAC1B,KAAK,GAAG;AAAA,cAAA;AAAA,YAAA;AAAA,UACZ;AAAA,QAAA;AAAA,QAEDE,IACC,gBAAAG,EAAC,QAAA,EAAK,WAAU,0DACb,aACH,IACE;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV;"}