@mieweb/ui 0.6.1-dev.132 → 0.6.1-dev.134

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 (74) hide show
  1. package/README.md +46 -10
  2. package/dist/CsvBlock-7K6XVRYO.cjs +15 -0
  3. package/dist/CsvBlock-7K6XVRYO.cjs.map +1 -0
  4. package/dist/CsvBlock-AAV4QMSF.js +6 -0
  5. package/dist/CsvBlock-AAV4QMSF.js.map +1 -0
  6. package/dist/HtmlPreviewBlock-74XQF3KT.cjs +15 -0
  7. package/dist/HtmlPreviewBlock-74XQF3KT.cjs.map +1 -0
  8. package/dist/HtmlPreviewBlock-DNINC7RW.js +6 -0
  9. package/dist/HtmlPreviewBlock-DNINC7RW.js.map +1 -0
  10. package/dist/MermaidBlock-PKXGFGU6.js +6 -0
  11. package/dist/MermaidBlock-PKXGFGU6.js.map +1 -0
  12. package/dist/MermaidBlock-R4U7IHSZ.cjs +15 -0
  13. package/dist/MermaidBlock-R4U7IHSZ.cjs.map +1 -0
  14. package/dist/SurveyBlock-E52I4PW3.cjs +15 -0
  15. package/dist/SurveyBlock-E52I4PW3.cjs.map +1 -0
  16. package/dist/SurveyBlock-IULSBG22.js +6 -0
  17. package/dist/SurveyBlock-IULSBG22.js.map +1 -0
  18. package/dist/brands/index.cjs +12 -12
  19. package/dist/brands/index.js +3 -3
  20. package/dist/{chunk-2DS3RJ2D.js → chunk-33PO3J4O.js} +35 -3
  21. package/dist/chunk-33PO3J4O.js.map +1 -0
  22. package/dist/chunk-6RV7HQ4U.cjs +198 -0
  23. package/dist/chunk-6RV7HQ4U.cjs.map +1 -0
  24. package/dist/chunk-74NOFB34.cjs +137 -0
  25. package/dist/chunk-74NOFB34.cjs.map +1 -0
  26. package/dist/chunk-CPJIOOBM.js +196 -0
  27. package/dist/chunk-CPJIOOBM.js.map +1 -0
  28. package/dist/{chunk-R6PBBPU3.js → chunk-CYMZ2QS5.js} +2 -2
  29. package/dist/{chunk-R6PBBPU3.js.map → chunk-CYMZ2QS5.js.map} +1 -1
  30. package/dist/chunk-FGIIXD37.js +182 -0
  31. package/dist/chunk-FGIIXD37.js.map +1 -0
  32. package/dist/chunk-FZQAF3WA.cjs +154 -0
  33. package/dist/chunk-FZQAF3WA.cjs.map +1 -0
  34. package/dist/chunk-MHJMUKLA.js +89 -0
  35. package/dist/chunk-MHJMUKLA.js.map +1 -0
  36. package/dist/chunk-NCRYCG5G.js +135 -0
  37. package/dist/chunk-NCRYCG5G.js.map +1 -0
  38. package/dist/chunk-NSLR3B7K.js +318 -0
  39. package/dist/chunk-NSLR3B7K.js.map +1 -0
  40. package/dist/{chunk-Z6NRP4Z5.cjs → chunk-SNEQXSC5.cjs} +2 -2
  41. package/dist/{chunk-Z6NRP4Z5.cjs.map → chunk-SNEQXSC5.cjs.map} +1 -1
  42. package/dist/chunk-UL3PQ7HL.cjs +91 -0
  43. package/dist/chunk-UL3PQ7HL.cjs.map +1 -0
  44. package/dist/chunk-VO3NWSCQ.cjs +184 -0
  45. package/dist/chunk-VO3NWSCQ.cjs.map +1 -0
  46. package/dist/chunk-WGC3KOP7.js +148 -0
  47. package/dist/chunk-WGC3KOP7.js.map +1 -0
  48. package/dist/{chunk-IBZXDX4L.cjs → chunk-WHUD3XHR.cjs} +35 -3
  49. package/dist/chunk-WHUD3XHR.cjs.map +1 -0
  50. package/dist/chunk-Y65SK5Y2.cjs +338 -0
  51. package/dist/chunk-Y65SK5Y2.cjs.map +1 -0
  52. package/dist/components/Markdown/index.cjs +51 -0
  53. package/dist/components/Markdown/index.cjs.map +1 -0
  54. package/dist/components/Markdown/index.css +331 -0
  55. package/dist/components/Markdown/index.css.map +1 -0
  56. package/dist/components/Markdown/index.d.cts +82 -0
  57. package/dist/components/Markdown/index.d.ts +82 -0
  58. package/dist/components/Markdown/index.js +10 -0
  59. package/dist/components/Markdown/index.js.map +1 -0
  60. package/dist/components/Markdown/styles.css +351 -0
  61. package/dist/index.cjs +1244 -570
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.css +331 -0
  64. package/dist/index.css.map +1 -0
  65. package/dist/index.d.cts +137 -1
  66. package/dist/index.d.ts +137 -1
  67. package/dist/index.js +1091 -456
  68. package/dist/index.js.map +1 -1
  69. package/dist/styles.css +1 -1
  70. package/dist/tailwind-preset.cjs +4 -4
  71. package/dist/tailwind-preset.js +1 -1
  72. package/package.json +23 -2
  73. package/dist/chunk-2DS3RJ2D.js.map +0 -1
  74. package/dist/chunk-IBZXDX4L.cjs.map +0 -1
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ var chunkUL3PQ7HL_cjs = require('./chunk-UL3PQ7HL.cjs');
4
+ var chunkA2QVQF54_cjs = require('./chunk-A2QVQF54.cjs');
5
+ var lucideReact = require('lucide-react');
6
+ var react = require('react');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ function isFullDocument(html) {
10
+ return /<!DOCTYPE|<html/i.test(html);
11
+ }
12
+ function wrapFragment(html) {
13
+ return `<!DOCTYPE html>
14
+ <html>
15
+ <head><meta charset="utf-8"><style>body{margin:0;padding:8px;font-family:system-ui,sans-serif;font-size:14px}</style></head>
16
+ <body>${html}</body>
17
+ </html>`;
18
+ }
19
+ var HtmlPreviewBlock = ({
20
+ code,
21
+ id
22
+ }) => {
23
+ const [showPreview, setShowPreview] = react.useState(false);
24
+ const [expanded, setExpanded] = react.useState(false);
25
+ const iframeRef = react.useRef(null);
26
+ const [iframeHeight, setIframeHeight] = react.useState(200);
27
+ const srcdoc = isFullDocument(code) ? code : wrapFragment(code);
28
+ react.useEffect(() => {
29
+ function handleMessage(event) {
30
+ if (event.origin !== "null" && event.origin !== window.location.origin)
31
+ return;
32
+ if (event.source !== iframeRef.current?.contentWindow) return;
33
+ if (event.data?.type === "HTML_PREVIEW_RESIZE" && event.data?.id === id) {
34
+ setIframeHeight(Math.min(Math.max(event.data.height, 200), 4e3));
35
+ }
36
+ }
37
+ window.addEventListener("message", handleMessage);
38
+ return () => window.removeEventListener("message", handleMessage);
39
+ }, [id]);
40
+ react.useEffect(() => {
41
+ if (!expanded) return;
42
+ function handleKey(e) {
43
+ if (e.key === "Escape") setExpanded(false);
44
+ }
45
+ document.addEventListener("keydown", handleKey);
46
+ return () => document.removeEventListener("keydown", handleKey);
47
+ }, [expanded]);
48
+ const togglePreview = react.useCallback(() => setShowPreview((v) => !v), []);
49
+ const headerActions = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
50
+ /* @__PURE__ */ jsxRuntime.jsxs(
51
+ chunkA2QVQF54_cjs.Button,
52
+ {
53
+ variant: "ghost",
54
+ size: "sm",
55
+ onClick: togglePreview,
56
+ className: "h-7 gap-1 px-2 text-xs",
57
+ children: [
58
+ showPreview ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code, { className: "h-3 w-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "h-3 w-3" }),
59
+ showPreview ? "Code" : "Preview"
60
+ ]
61
+ }
62
+ ),
63
+ showPreview && /* @__PURE__ */ jsxRuntime.jsx(
64
+ chunkA2QVQF54_cjs.Button,
65
+ {
66
+ variant: "ghost",
67
+ size: "sm",
68
+ onClick: () => setExpanded(true),
69
+ className: "h-7 w-7 p-0",
70
+ "aria-label": "Expand preview",
71
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-3 w-3" })
72
+ }
73
+ )
74
+ ] });
75
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
76
+ /* @__PURE__ */ jsxRuntime.jsx(chunkUL3PQ7HL_cjs.FenceBlock, { code, language: "html", headerActions, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: showPreview ? /* @__PURE__ */ jsxRuntime.jsx(
77
+ "iframe",
78
+ {
79
+ ref: iframeRef,
80
+ srcDoc: srcdoc,
81
+ sandbox: "allow-scripts allow-forms",
82
+ referrerPolicy: "no-referrer",
83
+ className: "w-full border-0",
84
+ style: { height: iframeHeight },
85
+ title: "HTML Preview"
86
+ }
87
+ ) : /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto p-3 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) }) }) }),
88
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs(
89
+ "div",
90
+ {
91
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50",
92
+ role: "dialog",
93
+ "aria-modal": "true",
94
+ "aria-label": "HTML Preview",
95
+ children: [
96
+ /* @__PURE__ */ jsxRuntime.jsx(
97
+ "button",
98
+ {
99
+ type: "button",
100
+ "aria-label": "Close preview",
101
+ className: "absolute inset-0 h-full w-full cursor-default border-0 bg-transparent p-0",
102
+ onClick: () => setExpanded(false)
103
+ }
104
+ ),
105
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-[90vh] w-[90vw] overflow-hidden rounded-lg bg-white shadow-xl dark:bg-neutral-900", children: [
106
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-neutral-200 px-4 py-2 dark:border-neutral-700", children: [
107
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "HTML Preview" }),
108
+ /* @__PURE__ */ jsxRuntime.jsx(
109
+ chunkA2QVQF54_cjs.Button,
110
+ {
111
+ variant: "ghost",
112
+ size: "sm",
113
+ onClick: () => setExpanded(false),
114
+ children: "Close"
115
+ }
116
+ )
117
+ ] }),
118
+ /* @__PURE__ */ jsxRuntime.jsx(
119
+ "iframe",
120
+ {
121
+ srcDoc: srcdoc,
122
+ sandbox: "allow-scripts allow-forms",
123
+ referrerPolicy: "no-referrer",
124
+ className: "h-full w-full border-0",
125
+ title: "HTML Preview (expanded)"
126
+ }
127
+ )
128
+ ] })
129
+ ]
130
+ }
131
+ )
132
+ ] });
133
+ };
134
+
135
+ exports.HtmlPreviewBlock = HtmlPreviewBlock;
136
+ //# sourceMappingURL=chunk-74NOFB34.cjs.map
137
+ //# sourceMappingURL=chunk-74NOFB34.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Markdown/HtmlPreviewBlock.tsx"],"names":["useState","useRef","useEffect","useCallback","jsxs","Fragment","Button","jsx","Code","Eye","Maximize2","FenceBlock"],"mappings":"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","file":"chunk-74NOFB34.cjs","sourcesContent":["/** Sandboxed iframe preview for HTML code blocks. */\nimport { Code, Eye, Maximize2 } from 'lucide-react';\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { Button } from '../Button';\nimport { FenceBlock } from './FenceBlock';\n\ninterface HtmlPreviewBlockProps {\n code: string;\n id: string;\n}\n\nfunction isFullDocument(html: string): boolean {\n return /<!DOCTYPE|<html/i.test(html);\n}\n\nfunction wrapFragment(html: string): string {\n return `<!DOCTYPE html>\n<html>\n<head><meta charset=\"utf-8\"><style>body{margin:0;padding:8px;font-family:system-ui,sans-serif;font-size:14px}</style></head>\n<body>${html}</body>\n</html>`;\n}\n\nexport const HtmlPreviewBlock: React.FC<HtmlPreviewBlockProps> = ({\n code,\n id,\n}) => {\n const [showPreview, setShowPreview] = useState(false);\n const [expanded, setExpanded] = useState(false);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const [iframeHeight, setIframeHeight] = useState(200);\n\n const srcdoc = isFullDocument(code) ? code : wrapFragment(code);\n\n useEffect(() => {\n function handleMessage(event: globalThis.MessageEvent) {\n // Sandboxed srcDoc iframes report origin \"null\"; validate source to prevent spoofing.\n if (event.origin !== 'null' && event.origin !== window.location.origin)\n return;\n if (event.source !== iframeRef.current?.contentWindow) return;\n if (event.data?.type === 'HTML_PREVIEW_RESIZE' && event.data?.id === id) {\n setIframeHeight(Math.min(Math.max(event.data.height, 200), 4000));\n }\n }\n window.addEventListener('message', handleMessage);\n return () => window.removeEventListener('message', handleMessage);\n }, [id]);\n\n useEffect(() => {\n if (!expanded) return;\n function handleKey(e: KeyboardEvent) {\n if (e.key === 'Escape') setExpanded(false);\n }\n document.addEventListener('keydown', handleKey);\n return () => document.removeEventListener('keydown', handleKey);\n }, [expanded]);\n\n const togglePreview = useCallback(() => setShowPreview((v) => !v), []);\n\n const headerActions = (\n <>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={togglePreview}\n className=\"h-7 gap-1 px-2 text-xs\"\n >\n {showPreview ? (\n <Code className=\"h-3 w-3\" />\n ) : (\n <Eye className=\"h-3 w-3\" />\n )}\n {showPreview ? 'Code' : 'Preview'}\n </Button>\n {showPreview && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setExpanded(true)}\n className=\"h-7 w-7 p-0\"\n aria-label=\"Expand preview\"\n >\n <Maximize2 className=\"h-3 w-3\" />\n </Button>\n )}\n </>\n );\n\n return (\n <>\n <FenceBlock code={code} language=\"html\" headerActions={headerActions}>\n <div className=\"relative\">\n {showPreview ? (\n <iframe\n ref={iframeRef}\n srcDoc={srcdoc}\n // `allow-scripts` lets previewed HTML run its own JS (charts, demos,\n // and the optional resize-postMessage handshake). It is intentionally\n // NOT paired with `allow-same-origin`, so the content runs in an\n // opaque (null) origin with no access to this page, cookies, or storage.\n sandbox=\"allow-scripts allow-forms\"\n referrerPolicy=\"no-referrer\"\n className=\"w-full border-0\"\n style={{ height: iframeHeight }}\n title=\"HTML Preview\"\n />\n ) : (\n <pre className=\"overflow-x-auto p-3 text-sm\">\n <code>{code}</code>\n </pre>\n )}\n </div>\n </FenceBlock>\n\n {expanded && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"HTML Preview\"\n >\n <button\n type=\"button\"\n aria-label=\"Close preview\"\n className=\"absolute inset-0 h-full w-full cursor-default border-0 bg-transparent p-0\"\n onClick={() => setExpanded(false)}\n />\n <div className=\"relative h-[90vh] w-[90vw] overflow-hidden rounded-lg bg-white shadow-xl dark:bg-neutral-900\">\n <div className=\"flex items-center justify-between border-b border-neutral-200 px-4 py-2 dark:border-neutral-700\">\n <span className=\"text-sm font-medium\">HTML Preview</span>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setExpanded(false)}\n >\n Close\n </Button>\n </div>\n <iframe\n srcDoc={srcdoc}\n sandbox=\"allow-scripts allow-forms\"\n referrerPolicy=\"no-referrer\"\n className=\"h-full w-full border-0\"\n title=\"HTML Preview (expanded)\"\n />\n </div>\n </div>\n )}\n </>\n );\n};\n"]}
@@ -0,0 +1,196 @@
1
+ import { FenceBlock } from './chunk-MHJMUKLA.js';
2
+ import { useState, useEffect } from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ var yamlPromise = null;
6
+ function loadYaml() {
7
+ if (!yamlPromise) {
8
+ yamlPromise = import(
9
+ /* @vite-ignore */
10
+ 'js-yaml'
11
+ ).then((mod) => {
12
+ const api = mod.default ?? mod;
13
+ const load = api.load;
14
+ if (typeof load !== "function")
15
+ throw new Error("js-yaml load not found");
16
+ return load;
17
+ }).catch((err) => {
18
+ yamlPromise = null;
19
+ throw err;
20
+ });
21
+ }
22
+ return yamlPromise;
23
+ }
24
+ function tryParseJson(code) {
25
+ try {
26
+ return { ok: true, data: JSON.parse(code) };
27
+ } catch {
28
+ return { ok: false };
29
+ }
30
+ }
31
+ function elementsToParsed(data) {
32
+ if (!data || typeof data !== "object") {
33
+ return { fields: [], error: "Invalid survey data format" };
34
+ }
35
+ const obj = data;
36
+ let elements = [];
37
+ if (Array.isArray(obj.pages)) {
38
+ for (const page of obj.pages) {
39
+ if (Array.isArray(page.elements)) {
40
+ elements = elements.concat(page.elements);
41
+ }
42
+ }
43
+ } else if (Array.isArray(obj.elements)) {
44
+ elements = obj.elements;
45
+ } else if (Array.isArray(obj.questions)) {
46
+ elements = obj.questions;
47
+ } else if (Array.isArray(obj.fields)) {
48
+ elements = obj.fields;
49
+ } else {
50
+ return { fields: [], error: "No survey questions found in data" };
51
+ }
52
+ return { fields: elements, error: null };
53
+ }
54
+ var SurveyBlock = ({ code, id }) => {
55
+ const [parsed, setParsed] = useState(null);
56
+ useEffect(() => {
57
+ let cancelled = false;
58
+ const jsonResult = tryParseJson(code);
59
+ if (jsonResult.ok) {
60
+ setParsed(elementsToParsed(jsonResult.data));
61
+ return;
62
+ }
63
+ loadYaml().then((load) => {
64
+ try {
65
+ const data = load(code);
66
+ if (!cancelled) setParsed(elementsToParsed(data));
67
+ } catch (err) {
68
+ if (!cancelled)
69
+ setParsed({
70
+ fields: [],
71
+ error: `Failed to parse survey data: ${err.message}`
72
+ });
73
+ }
74
+ }).catch(() => {
75
+ if (!cancelled)
76
+ setParsed({
77
+ fields: [],
78
+ error: "Survey preview requires the `js-yaml` package for YAML input. Install it with `npm install js-yaml`."
79
+ });
80
+ });
81
+ return () => {
82
+ cancelled = true;
83
+ };
84
+ }, [code]);
85
+ if (!parsed) {
86
+ return /* @__PURE__ */ jsx(FenceBlock, { code, language: "survey", supportsRawView: true, children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-6 text-sm text-neutral-500", children: "Loading survey\u2026" }) });
87
+ }
88
+ const { fields, error } = parsed;
89
+ return /* @__PURE__ */ jsx(
90
+ FenceBlock,
91
+ {
92
+ code,
93
+ language: "survey",
94
+ supportsRawView: true,
95
+ error: error ?? void 0,
96
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
97
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-neutral-700 dark:text-neutral-300", children: "Survey Preview" }),
98
+ fields.map((field, i) => {
99
+ const fieldId = `${id}-field-${field.name ?? i}`;
100
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
101
+ /* @__PURE__ */ jsxs(
102
+ "label",
103
+ {
104
+ htmlFor: fieldId,
105
+ className: "block text-sm font-medium text-neutral-700 dark:text-neutral-300",
106
+ children: [
107
+ field.title ?? field.label ?? field.name,
108
+ field.isRequired && /* @__PURE__ */ jsx("span", { className: "ml-1 text-red-500", children: "*" })
109
+ ]
110
+ }
111
+ ),
112
+ (field.type === "radiogroup" || field.type === "radio") && field.choices && /* @__PURE__ */ jsx("div", { className: "space-y-1", children: field.choices.map((choice, ci) => {
113
+ const value = typeof choice === "string" ? choice : choice.value;
114
+ const text = typeof choice === "string" ? choice : choice.text;
115
+ return /* @__PURE__ */ jsxs(
116
+ "label",
117
+ {
118
+ className: "flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400",
119
+ children: [
120
+ /* @__PURE__ */ jsx(
121
+ "input",
122
+ {
123
+ type: "radio",
124
+ name: `${id}-${field.name}`,
125
+ value,
126
+ disabled: true,
127
+ className: "accent-primary-500"
128
+ }
129
+ ),
130
+ text
131
+ ]
132
+ },
133
+ ci
134
+ );
135
+ }) }),
136
+ (field.type === "checkbox" || field.type === "boolean") && /* @__PURE__ */ jsx(
137
+ "input",
138
+ {
139
+ id: fieldId,
140
+ type: "checkbox",
141
+ disabled: true,
142
+ className: "accent-primary-500"
143
+ }
144
+ ),
145
+ (field.type === "text" || field.type === "comment") && /* @__PURE__ */ jsx(
146
+ "input",
147
+ {
148
+ id: fieldId,
149
+ type: "text",
150
+ disabled: true,
151
+ placeholder: field.title ?? field.name,
152
+ className: "w-full rounded-md border border-neutral-300 bg-neutral-100 px-3 py-1.5 text-sm dark:border-neutral-600 dark:bg-neutral-800"
153
+ }
154
+ ),
155
+ field.type === "rating" && /* @__PURE__ */ jsx(
156
+ "div",
157
+ {
158
+ role: "img",
159
+ className: "flex gap-1",
160
+ "aria-label": "Rating: 0 out of 5",
161
+ children: [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ jsx(
162
+ "span",
163
+ {
164
+ "aria-hidden": "true",
165
+ className: "text-lg text-neutral-500 dark:text-neutral-500",
166
+ children: "\u2605"
167
+ },
168
+ n
169
+ ))
170
+ }
171
+ ),
172
+ field.type === "dropdown" && field.choices && /* @__PURE__ */ jsx(
173
+ "select",
174
+ {
175
+ id: fieldId,
176
+ name: fieldId,
177
+ disabled: true,
178
+ className: "w-full rounded-md border border-neutral-300 bg-neutral-100 px-3 py-1.5 text-sm dark:border-neutral-600 dark:bg-neutral-800",
179
+ children: field.choices.map((choice, ci) => {
180
+ const value = typeof choice === "string" ? choice : choice.value;
181
+ const text = typeof choice === "string" ? choice : choice.text;
182
+ return /* @__PURE__ */ jsx("option", { value, children: text }, ci);
183
+ })
184
+ }
185
+ )
186
+ ] }, field.name ?? i);
187
+ }),
188
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-neutral-600 dark:text-neutral-400", children: "This is a preview. Interactive survey support coming soon." })
189
+ ] })
190
+ }
191
+ );
192
+ };
193
+
194
+ export { SurveyBlock };
195
+ //# sourceMappingURL=chunk-CPJIOOBM.js.map
196
+ //# sourceMappingURL=chunk-CPJIOOBM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Markdown/SurveyBlock.tsx"],"names":[],"mappings":"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","file":"chunk-CPJIOOBM.js","sourcesContent":["/** Survey/questionnaire preview from JSON or YAML. Requires `js-yaml` for YAML input. */\nimport React, { useEffect, useState } from 'react';\n\nimport { FenceBlock } from './FenceBlock';\n\ninterface SurveyBlockProps {\n code: string;\n id: string;\n}\n\ninterface SurveyField {\n type: string;\n name: string;\n title?: string;\n label?: string;\n choices?: Array<string | { value: string; text: string }>;\n isRequired?: boolean;\n}\n\ninterface ParsedSurvey {\n fields: SurveyField[];\n error: string | null;\n}\n\ntype YamlLoadFn = (input: string) => unknown;\nlet yamlPromise: Promise<YamlLoadFn> | null = null;\nfunction loadYaml(): Promise<YamlLoadFn> {\n if (!yamlPromise) {\n yamlPromise = import(/* @vite-ignore */ 'js-yaml')\n .then((mod) => {\n const api =\n (mod as { default?: { load?: YamlLoadFn }; load?: YamlLoadFn })\n .default ?? mod;\n const load = (api as { load?: YamlLoadFn }).load;\n if (typeof load !== 'function')\n throw new Error('js-yaml load not found');\n return load;\n })\n .catch((err) => {\n yamlPromise = null;\n throw err;\n });\n }\n return yamlPromise;\n}\n\nfunction tryParseJson(\n code: string\n): { ok: true; data: unknown } | { ok: false } {\n try {\n return { ok: true, data: JSON.parse(code) };\n } catch {\n return { ok: false };\n }\n}\n\nfunction elementsToParsed(data: unknown): ParsedSurvey {\n if (!data || typeof data !== 'object') {\n return { fields: [], error: 'Invalid survey data format' };\n }\n const obj = data as Record<string, unknown>;\n\n let elements: unknown[] = [];\n if (Array.isArray(obj.pages)) {\n for (const page of obj.pages as Array<Record<string, unknown>>) {\n if (Array.isArray(page.elements)) {\n elements = elements.concat(page.elements);\n }\n }\n } else if (Array.isArray(obj.elements)) {\n elements = obj.elements;\n } else if (Array.isArray(obj.questions)) {\n elements = obj.questions;\n } else if (Array.isArray(obj.fields)) {\n elements = obj.fields;\n } else {\n return { fields: [], error: 'No survey questions found in data' };\n }\n\n return { fields: elements as SurveyField[], error: null };\n}\n\nexport const SurveyBlock: React.FC<SurveyBlockProps> = ({ code, id }) => {\n const [parsed, setParsed] = useState<ParsedSurvey | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n\n const jsonResult = tryParseJson(code);\n if (jsonResult.ok) {\n setParsed(elementsToParsed(jsonResult.data));\n return;\n }\n\n loadYaml()\n .then((load) => {\n try {\n const data = load(code);\n if (!cancelled) setParsed(elementsToParsed(data));\n } catch (err) {\n if (!cancelled)\n setParsed({\n fields: [],\n error: `Failed to parse survey data: ${(err as Error).message}`,\n });\n }\n })\n .catch(() => {\n if (!cancelled)\n setParsed({\n fields: [],\n error:\n 'Survey preview requires the `js-yaml` package for YAML input. Install it with `npm install js-yaml`.',\n });\n });\n return () => {\n cancelled = true;\n };\n }, [code]);\n\n if (!parsed) {\n return (\n <FenceBlock code={code} language=\"survey\" supportsRawView>\n <div className=\"flex items-center justify-center p-6 text-sm text-neutral-500\">\n Loading survey…\n </div>\n </FenceBlock>\n );\n }\n\n const { fields, error } = parsed;\n\n return (\n <FenceBlock\n code={code}\n language=\"survey\"\n supportsRawView\n error={error ?? undefined}\n >\n <div className=\"space-y-4 p-4\">\n <h4 className=\"text-sm font-medium text-neutral-700 dark:text-neutral-300\">\n Survey Preview\n </h4>\n {fields.map((field, i) => {\n const fieldId = `${id}-field-${field.name ?? i}`;\n return (\n <div key={field.name ?? i} className=\"space-y-1.5\">\n <label\n htmlFor={fieldId}\n className=\"block text-sm font-medium text-neutral-700 dark:text-neutral-300\"\n >\n {field.title ?? field.label ?? field.name}\n {field.isRequired && (\n <span className=\"ml-1 text-red-500\">*</span>\n )}\n </label>\n\n {(field.type === 'radiogroup' || field.type === 'radio') &&\n field.choices && (\n <div className=\"space-y-1\">\n {field.choices.map((choice, ci) => {\n const value =\n typeof choice === 'string' ? choice : choice.value;\n const text =\n typeof choice === 'string' ? choice : choice.text;\n return (\n <label\n key={ci}\n className=\"flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400\"\n >\n <input\n type=\"radio\"\n name={`${id}-${field.name}`}\n value={value}\n disabled\n className=\"accent-primary-500\"\n />\n {text}\n </label>\n );\n })}\n </div>\n )}\n\n {(field.type === 'checkbox' || field.type === 'boolean') && (\n <input\n id={fieldId}\n type=\"checkbox\"\n disabled\n className=\"accent-primary-500\"\n />\n )}\n\n {(field.type === 'text' || field.type === 'comment') && (\n <input\n id={fieldId}\n type=\"text\"\n disabled\n placeholder={field.title ?? field.name}\n className=\"w-full rounded-md border border-neutral-300 bg-neutral-100 px-3 py-1.5 text-sm dark:border-neutral-600 dark:bg-neutral-800\"\n />\n )}\n\n {field.type === 'rating' && (\n <div\n role=\"img\"\n className=\"flex gap-1\"\n aria-label=\"Rating: 0 out of 5\"\n >\n {[1, 2, 3, 4, 5].map((n) => (\n <span\n key={n}\n aria-hidden=\"true\"\n className=\"text-lg text-neutral-500 dark:text-neutral-500\"\n >\n ★\n </span>\n ))}\n </div>\n )}\n\n {field.type === 'dropdown' && field.choices && (\n <select\n id={fieldId}\n name={fieldId}\n disabled\n className=\"w-full rounded-md border border-neutral-300 bg-neutral-100 px-3 py-1.5 text-sm dark:border-neutral-600 dark:bg-neutral-800\"\n >\n {field.choices.map((choice, ci) => {\n const value =\n typeof choice === 'string' ? choice : choice.value;\n const text =\n typeof choice === 'string' ? choice : choice.text;\n return (\n <option key={ci} value={value}>\n {text}\n </option>\n );\n })}\n </select>\n )}\n </div>\n );\n })}\n <p className=\"text-xs text-neutral-600 dark:text-neutral-400\">\n This is a preview. Interactive survey support coming soon.\n </p>\n </div>\n </FenceBlock>\n );\n};\n"]}
@@ -11,5 +11,5 @@ var brands = {
11
11
  };
12
12
 
13
13
  export { brands };
14
- //# sourceMappingURL=chunk-R6PBBPU3.js.map
15
- //# sourceMappingURL=chunk-R6PBBPU3.js.map
14
+ //# sourceMappingURL=chunk-CYMZ2QS5.js.map
15
+ //# sourceMappingURL=chunk-CYMZ2QS5.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/brands/index.ts"],"names":[],"mappings":";AAkCO,IAAM,MAAA,GAAS;AAAA,EACpB,QAAA,EAAU,MAAM,OAAO,sBAAY,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa,CAAA;AAAA,EAChE,IAAA,EAAM,MAAM,OAAO,kBAAQ,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,CAAA;AAAA,EACpD,OAAA,EAAS,MAAM,OAAO,uBAAW,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,YAAY,CAAA;AAAA,EAC7D,mBAAA,EAAqB,MACnB,OAAO,iCAAqB,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,qBAAqB,CAAA;AAAA,EACnE,MAAA,EAAQ,MAAM,OAAO,sBAAU,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA;AAAA,EAC1D,MAAA,EAAQ,MAAM,OAAO,oBAAU,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA;AAAA,EAC1D,UAAA,EAAY,MAAM,OAAO,0BAAc,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,eAAe,CAAA;AAAA,EACtE,QAAA,EAAU,MAAM,OAAO,wBAAY,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa;AAClE","file":"chunk-R6PBBPU3.js","sourcesContent":["/**\n * @mieweb/ui Brand System\n *\n * Export all brand-related types, utilities, and configurations.\n */\n\n// Types and utilities\nexport type {\n BrandConfig,\n BrandColors,\n BrandTypography,\n BrandBorderRadius,\n BrandBoxShadow,\n ColorScale,\n SemanticColors,\n} from './types';\n\nexport {\n generateBrandCSS,\n generateTailwindTheme,\n createBrandPreset,\n} from './types';\n\n// Brand configurations\nexport { bluehiveBrand } from './bluehive';\nexport { ccmeBrand } from './ccme';\nexport { defaultBrand } from './default';\nexport { enterpriseHealthBrand } from './enterprise-health';\nexport { miewebBrand } from './mieweb';\nexport { ozwellBrand } from './ozwell';\nexport { wagglelineBrand } from './waggleline';\nexport { webchartBrand } from './webchart';\n\n// All brands for iteration\nexport const brands = {\n bluehive: () => import('./bluehive').then((m) => m.bluehiveBrand),\n ccme: () => import('./ccme').then((m) => m.ccmeBrand),\n default: () => import('./default').then((m) => m.defaultBrand),\n 'enterprise-health': () =>\n import('./enterprise-health').then((m) => m.enterpriseHealthBrand),\n mieweb: () => import('./mieweb').then((m) => m.miewebBrand),\n ozwell: () => import('./ozwell').then((m) => m.ozwellBrand),\n waggleline: () => import('./waggleline').then((m) => m.wagglelineBrand),\n webchart: () => import('./webchart').then((m) => m.webchartBrand),\n} as const;\n"]}
1
+ {"version":3,"sources":["../src/brands/index.ts"],"names":[],"mappings":";AAkCO,IAAM,MAAA,GAAS;AAAA,EACpB,QAAA,EAAU,MAAM,OAAO,sBAAY,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa,CAAA;AAAA,EAChE,IAAA,EAAM,MAAM,OAAO,kBAAQ,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,CAAA;AAAA,EACpD,OAAA,EAAS,MAAM,OAAO,uBAAW,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,YAAY,CAAA;AAAA,EAC7D,mBAAA,EAAqB,MACnB,OAAO,iCAAqB,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,qBAAqB,CAAA;AAAA,EACnE,MAAA,EAAQ,MAAM,OAAO,sBAAU,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA;AAAA,EAC1D,MAAA,EAAQ,MAAM,OAAO,oBAAU,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA;AAAA,EAC1D,UAAA,EAAY,MAAM,OAAO,0BAAc,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,eAAe,CAAA;AAAA,EACtE,QAAA,EAAU,MAAM,OAAO,wBAAY,EAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,aAAa;AAClE","file":"chunk-CYMZ2QS5.js","sourcesContent":["/**\n * @mieweb/ui Brand System\n *\n * Export all brand-related types, utilities, and configurations.\n */\n\n// Types and utilities\nexport type {\n BrandConfig,\n BrandColors,\n BrandTypography,\n BrandBorderRadius,\n BrandBoxShadow,\n ColorScale,\n SemanticColors,\n} from './types';\n\nexport {\n generateBrandCSS,\n generateTailwindTheme,\n createBrandPreset,\n} from './types';\n\n// Brand configurations\nexport { bluehiveBrand } from './bluehive';\nexport { ccmeBrand } from './ccme';\nexport { defaultBrand } from './default';\nexport { enterpriseHealthBrand } from './enterprise-health';\nexport { miewebBrand } from './mieweb';\nexport { ozwellBrand } from './ozwell';\nexport { wagglelineBrand } from './waggleline';\nexport { webchartBrand } from './webchart';\n\n// All brands for iteration\nexport const brands = {\n bluehive: () => import('./bluehive').then((m) => m.bluehiveBrand),\n ccme: () => import('./ccme').then((m) => m.ccmeBrand),\n default: () => import('./default').then((m) => m.defaultBrand),\n 'enterprise-health': () =>\n import('./enterprise-health').then((m) => m.enterpriseHealthBrand),\n mieweb: () => import('./mieweb').then((m) => m.miewebBrand),\n ozwell: () => import('./ozwell').then((m) => m.ozwellBrand),\n waggleline: () => import('./waggleline').then((m) => m.wagglelineBrand),\n webchart: () => import('./webchart').then((m) => m.webchartBrand),\n} as const;\n"]}
@@ -0,0 +1,182 @@
1
+ import { FenceBlock } from './chunk-MHJMUKLA.js';
2
+ import { Button } from './chunk-ZVSW2KS6.js';
3
+ import { Download } from 'lucide-react';
4
+ import { useState, useEffect, useMemo, useCallback } from 'react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var papaPromise = null;
8
+ function loadPapa() {
9
+ if (!papaPromise) {
10
+ papaPromise = import(
11
+ /* @vite-ignore */
12
+ 'papaparse'
13
+ ).then((mod) => {
14
+ const api = mod.default ?? mod;
15
+ if (!api?.parse) throw new Error("papaparse export not found");
16
+ return api;
17
+ }).catch((err) => {
18
+ papaPromise = null;
19
+ throw err;
20
+ });
21
+ }
22
+ return papaPromise;
23
+ }
24
+ var CsvBlock = ({ code, id }) => {
25
+ const [sortConfig, setSortConfig] = useState(null);
26
+ const [papa, setPapa] = useState(null);
27
+ const [loadError, setLoadError] = useState(null);
28
+ useEffect(() => {
29
+ let cancelled = false;
30
+ loadPapa().then((api) => {
31
+ if (!cancelled) setPapa(api);
32
+ }).catch(() => {
33
+ if (!cancelled)
34
+ setLoadError(
35
+ "CSV preview requires the `papaparse` package. Install it with `npm install papaparse`."
36
+ );
37
+ });
38
+ return () => {
39
+ cancelled = true;
40
+ };
41
+ }, []);
42
+ const parsed = useMemo(() => {
43
+ if (!papa) return { headers: [], rows: [], error: null };
44
+ try {
45
+ const result = papa.parse(code, {
46
+ header: true,
47
+ dynamicTyping: true,
48
+ skipEmptyLines: "greedy"
49
+ });
50
+ if (result.errors?.length) {
51
+ return {
52
+ headers: result.meta.fields ?? [],
53
+ rows: result.data,
54
+ error: result.errors.map((e) => e.message).join("; ")
55
+ };
56
+ }
57
+ return {
58
+ headers: result.meta.fields ?? [],
59
+ rows: result.data,
60
+ error: null
61
+ };
62
+ } catch (err) {
63
+ return { headers: [], rows: [], error: err.message };
64
+ }
65
+ }, [code, papa]);
66
+ const sortedRows = useMemo(() => {
67
+ if (!sortConfig || parsed.error) return parsed.rows;
68
+ return [...parsed.rows].sort((a, b) => {
69
+ const aVal = a[sortConfig.column];
70
+ const bVal = b[sortConfig.column];
71
+ if (aVal == null && bVal == null) return 0;
72
+ if (aVal == null) return 1;
73
+ if (bVal == null) return -1;
74
+ const cmp = typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal));
75
+ return sortConfig.direction === "asc" ? cmp : -cmp;
76
+ });
77
+ }, [parsed.rows, sortConfig, parsed.error]);
78
+ const handleSort = useCallback((column) => {
79
+ setSortConfig((prev) => {
80
+ if (prev?.column === column) {
81
+ return prev.direction === "asc" ? { column, direction: "desc" } : null;
82
+ }
83
+ return { column, direction: "asc" };
84
+ });
85
+ }, []);
86
+ const handleExportCsv = useCallback(() => {
87
+ if (!papa) return;
88
+ const csvContent = papa.unparse(sortedRows);
89
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
90
+ const url = URL.createObjectURL(blob);
91
+ const link = document.createElement("a");
92
+ link.href = url;
93
+ link.download = `data-${id}.csv`;
94
+ document.body.appendChild(link);
95
+ link.click();
96
+ document.body.removeChild(link);
97
+ setTimeout(() => URL.revokeObjectURL(url), 100);
98
+ }, [sortedRows, id, papa]);
99
+ if (loadError) {
100
+ return /* @__PURE__ */ jsx(FenceBlock, { code, language: "csv", error: loadError, children: /* @__PURE__ */ jsx("div", {}) });
101
+ }
102
+ if (!papa) {
103
+ return /* @__PURE__ */ jsx(FenceBlock, { code, language: "csv", supportsRawView: true, children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-6 text-sm text-neutral-500", children: "Loading CSV parser\u2026" }) });
104
+ }
105
+ if (parsed.error) {
106
+ return /* @__PURE__ */ jsx(FenceBlock, { code, language: "csv", error: parsed.error, children: /* @__PURE__ */ jsx("div", {}) });
107
+ }
108
+ return /* @__PURE__ */ jsx(FenceBlock, { code, language: "csv", supportsRawView: true, children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
109
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-neutral-200 px-3 py-1.5 dark:border-neutral-700", children: [
110
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-neutral-500 dark:text-neutral-400", children: [
111
+ sortedRows.length,
112
+ " row",
113
+ sortedRows.length !== 1 ? "s" : "",
114
+ " \xD7",
115
+ " ",
116
+ parsed.headers.length,
117
+ " column",
118
+ parsed.headers.length !== 1 ? "s" : ""
119
+ ] }),
120
+ /* @__PURE__ */ jsxs(
121
+ Button,
122
+ {
123
+ variant: "ghost",
124
+ size: "sm",
125
+ onClick: handleExportCsv,
126
+ "aria-label": "Export CSV",
127
+ className: "h-7 gap-1 px-2 text-xs",
128
+ children: [
129
+ /* @__PURE__ */ jsx(Download, { className: "h-3 w-3" }),
130
+ "Export"
131
+ ]
132
+ }
133
+ )
134
+ ] }),
135
+ /* @__PURE__ */ jsx("div", { className: "max-h-96 overflow-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
136
+ /* @__PURE__ */ jsx("thead", { className: "sticky top-0 bg-neutral-100 dark:bg-neutral-800", children: /* @__PURE__ */ jsx("tr", { children: parsed.headers.map((header) => {
137
+ const isSorted = sortConfig?.column === header;
138
+ const ariaSort = isSorted ? sortConfig.direction === "asc" ? "ascending" : "descending" : "none";
139
+ return /* @__PURE__ */ jsx(
140
+ "th",
141
+ {
142
+ scope: "col",
143
+ "aria-sort": ariaSort,
144
+ className: "px-3 py-2 text-left text-xs font-medium whitespace-nowrap text-neutral-600 dark:text-neutral-400",
145
+ children: /* @__PURE__ */ jsxs(
146
+ "button",
147
+ {
148
+ type: "button",
149
+ onClick: () => handleSort(header),
150
+ className: "flex w-full cursor-pointer items-center gap-1 text-left font-medium select-none hover:text-neutral-900 dark:hover:text-neutral-200",
151
+ children: [
152
+ header,
153
+ isSorted && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: sortConfig.direction === "asc" ? "\u2191" : "\u2193" })
154
+ ]
155
+ }
156
+ )
157
+ },
158
+ header
159
+ );
160
+ }) }) }),
161
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-neutral-200 dark:divide-neutral-700", children: sortedRows.map((row, i) => /* @__PURE__ */ jsx(
162
+ "tr",
163
+ {
164
+ className: "hover:bg-neutral-50 dark:hover:bg-neutral-800/50",
165
+ children: parsed.headers.map((header) => /* @__PURE__ */ jsx(
166
+ "td",
167
+ {
168
+ className: "px-3 py-1.5 whitespace-nowrap text-neutral-700 dark:text-neutral-300",
169
+ children: row[header] != null ? String(row[header]) : ""
170
+ },
171
+ header
172
+ ))
173
+ },
174
+ i
175
+ )) })
176
+ ] }) })
177
+ ] }) });
178
+ };
179
+
180
+ export { CsvBlock };
181
+ //# sourceMappingURL=chunk-FGIIXD37.js.map
182
+ //# sourceMappingURL=chunk-FGIIXD37.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Markdown/CsvBlock.tsx"],"names":[],"mappings":"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","file":"chunk-FGIIXD37.js","sourcesContent":["/** CSV data rendered as a sortable table. Requires `papaparse`. */\nimport { Download } from 'lucide-react';\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { Button } from '../Button';\nimport { FenceBlock } from './FenceBlock';\n\ninterface CsvBlockProps {\n code: string;\n id: string;\n}\n\ninterface SortConfig {\n column: string;\n direction: 'asc' | 'desc';\n}\n\ntype CsvRow = Record<string, unknown>;\n// Minimal surface of papaparse we actually use, avoiding overload conflicts.\ninterface PapaApi {\n parse(\n input: string,\n config?: Record<string, unknown>\n ): {\n meta: { fields?: string[] };\n data: CsvRow[];\n errors: Array<{ message: string }>;\n };\n unparse(data: unknown): string;\n}\n\nlet papaPromise: Promise<PapaApi> | null = null;\nfunction loadPapa() {\n if (!papaPromise) {\n papaPromise = import(/* @vite-ignore */ 'papaparse')\n .then((mod: { default?: PapaApi } & Partial<PapaApi>) => {\n const api: PapaApi = (mod.default ?? mod) as PapaApi;\n if (!api?.parse) throw new Error('papaparse export not found');\n return api;\n })\n .catch((err) => {\n papaPromise = null;\n throw err as Error;\n });\n }\n return papaPromise;\n}\n\ninterface ParsedCsv {\n headers: string[];\n rows: CsvRow[];\n error: string | null;\n}\n\nexport const CsvBlock: React.FC<CsvBlockProps> = ({ code, id }) => {\n const [sortConfig, setSortConfig] = useState<SortConfig | null>(null);\n const [papa, setPapa] = useState<PapaApi | null>(null);\n const [loadError, setLoadError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n loadPapa()\n .then((api) => {\n if (!cancelled) setPapa(api);\n })\n .catch(() => {\n if (!cancelled)\n setLoadError(\n 'CSV preview requires the `papaparse` package. Install it with `npm install papaparse`.'\n );\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const parsed = useMemo<ParsedCsv>(() => {\n if (!papa) return { headers: [], rows: [], error: null };\n try {\n const result = papa.parse(code, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: 'greedy',\n });\n if (result.errors?.length) {\n return {\n headers: result.meta.fields ?? [],\n rows: result.data,\n error: result.errors.map((e) => e.message).join('; '),\n };\n }\n return {\n headers: result.meta.fields ?? [],\n rows: result.data,\n error: null,\n };\n } catch (err) {\n return { headers: [], rows: [], error: (err as Error).message };\n }\n }, [code, papa]);\n\n const sortedRows = useMemo(() => {\n if (!sortConfig || parsed.error) return parsed.rows;\n return [...parsed.rows].sort((a, b) => {\n const aVal = a[sortConfig.column];\n const bVal = b[sortConfig.column];\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n const cmp =\n typeof aVal === 'number' && typeof bVal === 'number'\n ? aVal - bVal\n : String(aVal).localeCompare(String(bVal));\n return sortConfig.direction === 'asc' ? cmp : -cmp;\n });\n }, [parsed.rows, sortConfig, parsed.error]);\n\n const handleSort = useCallback((column: string) => {\n setSortConfig((prev) => {\n if (prev?.column === column) {\n return prev.direction === 'asc' ? { column, direction: 'desc' } : null;\n }\n return { column, direction: 'asc' };\n });\n }, []);\n\n const handleExportCsv = useCallback(() => {\n if (!papa) return;\n const csvContent = papa.unparse(sortedRows);\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `data-${id}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n setTimeout(() => URL.revokeObjectURL(url), 100);\n }, [sortedRows, id, papa]);\n\n if (loadError) {\n return (\n <FenceBlock code={code} language=\"csv\" error={loadError}>\n <div />\n </FenceBlock>\n );\n }\n\n if (!papa) {\n return (\n <FenceBlock code={code} language=\"csv\" supportsRawView>\n <div className=\"flex items-center justify-center p-6 text-sm text-neutral-500\">\n Loading CSV parser…\n </div>\n </FenceBlock>\n );\n }\n\n if (parsed.error) {\n return (\n <FenceBlock code={code} language=\"csv\" error={parsed.error}>\n <div />\n </FenceBlock>\n );\n }\n\n return (\n <FenceBlock code={code} language=\"csv\" supportsRawView>\n <div className=\"relative\">\n <div className=\"flex items-center justify-between border-b border-neutral-200 px-3 py-1.5 dark:border-neutral-700\">\n <span className=\"text-xs text-neutral-500 dark:text-neutral-400\">\n {sortedRows.length} row{sortedRows.length !== 1 ? 's' : ''} ×{' '}\n {parsed.headers.length} column\n {parsed.headers.length !== 1 ? 's' : ''}\n </span>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleExportCsv}\n aria-label=\"Export CSV\"\n className=\"h-7 gap-1 px-2 text-xs\"\n >\n <Download className=\"h-3 w-3\" />\n Export\n </Button>\n </div>\n\n <div className=\"max-h-96 overflow-auto\">\n <table className=\"min-w-full text-sm\">\n <thead className=\"sticky top-0 bg-neutral-100 dark:bg-neutral-800\">\n <tr>\n {parsed.headers.map((header) => {\n const isSorted = sortConfig?.column === header;\n const ariaSort = isSorted\n ? sortConfig.direction === 'asc'\n ? 'ascending'\n : 'descending'\n : 'none';\n return (\n <th\n key={header}\n scope=\"col\"\n aria-sort={ariaSort}\n className=\"px-3 py-2 text-left text-xs font-medium whitespace-nowrap text-neutral-600 dark:text-neutral-400\"\n >\n <button\n type=\"button\"\n onClick={() => handleSort(header)}\n className=\"flex w-full cursor-pointer items-center gap-1 text-left font-medium select-none hover:text-neutral-900 dark:hover:text-neutral-200\"\n >\n {header}\n {isSorted && (\n <span aria-hidden=\"true\">\n {sortConfig.direction === 'asc' ? '↑' : '↓'}\n </span>\n )}\n </button>\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-neutral-200 dark:divide-neutral-700\">\n {sortedRows.map((row, i) => (\n <tr\n key={i}\n className=\"hover:bg-neutral-50 dark:hover:bg-neutral-800/50\"\n >\n {parsed.headers.map((header) => (\n <td\n key={header}\n className=\"px-3 py-1.5 whitespace-nowrap text-neutral-700 dark:text-neutral-300\"\n >\n {row[header] != null ? String(row[header]) : ''}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </FenceBlock>\n );\n};\n"]}