@2londres/shareable-preview 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.
package/dist/index.js ADDED
@@ -0,0 +1,1184 @@
1
+ 'use strict';
2
+
3
+ var React5 = require('react');
4
+ var clsx = require('clsx');
5
+ var tailwindMerge = require('tailwind-merge');
6
+ var reactSlot = require('@radix-ui/react-slot');
7
+ var classVarianceAuthority = require('class-variance-authority');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+ var DialogPrimitive = require('@radix-ui/react-dialog');
10
+ var lucideReact = require('lucide-react');
11
+ var TabsPrimitive = require('@radix-ui/react-tabs');
12
+
13
+ function _interopNamespace(e) {
14
+ if (e && e.__esModule) return e;
15
+ var n = Object.create(null);
16
+ if (e) {
17
+ Object.keys(e).forEach(function (k) {
18
+ if (k !== 'default') {
19
+ var d = Object.getOwnPropertyDescriptor(e, k);
20
+ Object.defineProperty(n, k, d.get ? d : {
21
+ enumerable: true,
22
+ get: function () { return e[k]; }
23
+ });
24
+ }
25
+ });
26
+ }
27
+ n.default = e;
28
+ return Object.freeze(n);
29
+ }
30
+
31
+ var React5__namespace = /*#__PURE__*/_interopNamespace(React5);
32
+ var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
33
+ var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
34
+
35
+ // src/ShareablePreview.tsx
36
+ function cn(...inputs) {
37
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
38
+ }
39
+ var buttonVariants = classVarianceAuthority.cva(
40
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
41
+ {
42
+ variants: {
43
+ variant: {
44
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
45
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
46
+ success: "bg-green-600 text-destructive-foreground hover:bg-green-900/90",
47
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
48
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
49
+ warning: "bg-yellow-500 text-white hover:bg-yellow-500/80",
50
+ ghost: "hover:bg-accent hover:text-accent-foreground",
51
+ link: "text-primary underline-offset-4 hover:underline"
52
+ },
53
+ size: {
54
+ default: "h-10 px-4 py-2",
55
+ sm: "h-9 rounded-md px-3",
56
+ lg: "h-11 rounded-md px-8",
57
+ icon: "h-10 w-10"
58
+ }
59
+ },
60
+ defaultVariants: {
61
+ variant: "default",
62
+ size: "default"
63
+ }
64
+ }
65
+ );
66
+ var Button = React5__namespace.forwardRef(
67
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
68
+ const Comp = asChild ? reactSlot.Slot : "button";
69
+ return /* @__PURE__ */ jsxRuntime.jsx(
70
+ Comp,
71
+ {
72
+ className: cn(buttonVariants({ variant, size, className })),
73
+ ref,
74
+ ...props
75
+ }
76
+ );
77
+ }
78
+ );
79
+ Button.displayName = "Button";
80
+ var Card = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
81
+ "div",
82
+ {
83
+ ref,
84
+ className: cn(
85
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
86
+ className
87
+ ),
88
+ ...props
89
+ }
90
+ ));
91
+ Card.displayName = "Card";
92
+ var Dialog = DialogPrimitive__namespace.Root;
93
+ var DialogTrigger = DialogPrimitive__namespace.Trigger;
94
+ var DialogPortal = DialogPrimitive__namespace.Portal;
95
+ var DialogOverlay = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
96
+ DialogPrimitive__namespace.Overlay,
97
+ {
98
+ ref,
99
+ className: cn(
100
+ "fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-black/70 backdrop-blur-sm",
101
+ className
102
+ ),
103
+ ...props
104
+ }
105
+ ));
106
+ DialogOverlay.displayName = DialogPrimitive__namespace.Overlay.displayName ?? "DialogOverlay";
107
+ var DialogContent = React5__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { children: [
108
+ /* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
109
+ /* @__PURE__ */ jsxRuntime.jsxs(
110
+ DialogPrimitive__namespace.Content,
111
+ {
112
+ ref,
113
+ className: cn(
114
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
115
+ className
116
+ ),
117
+ ...props,
118
+ children: [
119
+ children,
120
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
121
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }),
122
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
123
+ ] })
124
+ ]
125
+ }
126
+ )
127
+ ] }));
128
+ DialogContent.displayName = DialogPrimitive__namespace.Content.displayName ?? "DialogContent";
129
+ var Tabs = TabsPrimitive__namespace.Root;
130
+ var TabsList = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
131
+ TabsPrimitive__namespace.List,
132
+ {
133
+ ref,
134
+ className: cn(
135
+ "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
136
+ className
137
+ ),
138
+ ...props
139
+ }
140
+ ));
141
+ TabsList.displayName = TabsPrimitive__namespace.List.displayName;
142
+ var TabsTrigger = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
143
+ TabsPrimitive__namespace.Trigger,
144
+ {
145
+ ref,
146
+ className: cn(
147
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
148
+ className
149
+ ),
150
+ ...props
151
+ }
152
+ ));
153
+ TabsTrigger.displayName = TabsPrimitive__namespace.Trigger.displayName;
154
+ var TabsContent = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
155
+ TabsPrimitive__namespace.Content,
156
+ {
157
+ ref,
158
+ className: cn(
159
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
160
+ className
161
+ ),
162
+ ...props
163
+ }
164
+ ));
165
+ TabsContent.displayName = TabsPrimitive__namespace.Content.displayName;
166
+ var gradientVariants = {
167
+ green: {
168
+ base: "from-green-600 to-green-700",
169
+ hover: "hover:from-green-700 hover:to-green-800",
170
+ shadow: "hover:shadow-green-400/50"
171
+ },
172
+ blue: {
173
+ base: "from-blue-600 to-blue-700",
174
+ hover: "hover:from-blue-700 hover:to-blue-800",
175
+ shadow: "hover:shadow-blue-400/50"
176
+ },
177
+ purple: {
178
+ base: "from-purple-600 to-purple-700",
179
+ hover: "hover:from-purple-700 hover:to-purple-800",
180
+ shadow: "hover:shadow-purple-400/50"
181
+ },
182
+ orange: {
183
+ base: "from-orange-600 to-orange-700",
184
+ hover: "hover:from-orange-700 hover:to-orange-800",
185
+ shadow: "hover:shadow-orange-400/50"
186
+ },
187
+ red: {
188
+ base: "from-red-600 to-red-700",
189
+ hover: "hover:from-red-700 hover:to-red-800",
190
+ shadow: "hover:shadow-red-400/50"
191
+ }
192
+ };
193
+ var ValidationButton = React5.forwardRef(
194
+ ({
195
+ children,
196
+ className,
197
+ withPulse = false,
198
+ withIndicator = true,
199
+ variant = "green",
200
+ icon: Icon = lucideReact.CheckCircle,
201
+ ...props
202
+ }, ref) => {
203
+ const gradients = gradientVariants[variant];
204
+ return /* @__PURE__ */ jsxRuntime.jsxs(
205
+ Button,
206
+ {
207
+ ref,
208
+ size: "lg",
209
+ className: cn(
210
+ "px-8 py-4 font-bold text-white border-0 shadow-xl transition-all duration-300 relative",
211
+ `bg-gradient-to-r ${gradients.base}`,
212
+ `${gradients.hover} hover:scale-105 hover:shadow-2xl ${gradients.shadow}`,
213
+ withPulse && "animate-pulse",
214
+ "focus:animate-none hover:animate-none",
215
+ className
216
+ ),
217
+ ...props,
218
+ children: [
219
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center mr-3 w-6 h-6 rounded-full bg-white/20", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-4 h-4" }) }),
220
+ children,
221
+ withIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1 -right-1 w-3 h-3 bg-yellow-400 rounded-full animate-ping" })
222
+ ]
223
+ }
224
+ );
225
+ }
226
+ );
227
+ ValidationButton.displayName = "ValidationButton";
228
+ function getValueType(value) {
229
+ if (value === null) return "null";
230
+ if (Array.isArray(value)) return "array";
231
+ if (typeof value === "object") return "object";
232
+ if (typeof value === "boolean") return "boolean";
233
+ if (typeof value === "number") return "number";
234
+ if (typeof value === "string") return "string";
235
+ return typeof value;
236
+ }
237
+ function isExpandable(value) {
238
+ if (value === null || value === void 0) return false;
239
+ if (typeof value === "object")
240
+ return Object.keys(value).length > 0;
241
+ return false;
242
+ }
243
+ function formatPrimitive(value) {
244
+ if (value === null) return "null";
245
+ if (typeof value === "string") return `"${value}"`;
246
+ if (typeof value === "boolean") return value ? "true" : "false";
247
+ if (typeof value === "number") return String(value);
248
+ return String(value);
249
+ }
250
+ function DataTreeNode({
251
+ nodeKey,
252
+ value,
253
+ level = 0,
254
+ maxLevel = 20,
255
+ maxDepthLabel = "... (max depth)"
256
+ }) {
257
+ const [isExpanded, setIsExpanded] = React5.useState(level < 2);
258
+ const expandable = isExpandable(value);
259
+ const valueType = getValueType(value);
260
+ const isArray = Array.isArray(value);
261
+ const entries = expandable ? isArray ? value.map((v, i) => [String(i), v]) : value !== null && value !== void 0 && typeof value === "object" ? Object.entries(value) : [] : [];
262
+ if (level > maxLevel) {
263
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs italic text-muted-foreground", children: [
264
+ nodeKey,
265
+ ": ",
266
+ maxDepthLabel
267
+ ] });
268
+ }
269
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-mono text-sm", children: [
270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 items-start", children: [
271
+ expandable ? /* @__PURE__ */ jsxRuntime.jsx(
272
+ "button",
273
+ {
274
+ onClick: () => setIsExpanded(!isExpanded),
275
+ className: "flex-shrink-0 p-0 transition-colors text-muted-foreground hover:text-foreground",
276
+ "aria-label": isExpanded ? "Collapse" : "Expand",
277
+ children: /* @__PURE__ */ jsxRuntime.jsx(
278
+ lucideReact.ChevronRight,
279
+ {
280
+ className: cn(
281
+ "h-4 w-4 transition-transform",
282
+ isExpanded && "rotate-90"
283
+ )
284
+ }
285
+ )
286
+ }
287
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-4" }),
288
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-shrink-0 font-semibold text-primary", children: [
289
+ nodeKey,
290
+ ":"
291
+ ] }),
292
+ !expandable && /* @__PURE__ */ jsxRuntime.jsx(
293
+ "span",
294
+ {
295
+ className: cn(
296
+ "text-foreground",
297
+ valueType === "string" && "text-green-600"
298
+ ),
299
+ children: formatPrimitive(value)
300
+ }
301
+ ),
302
+ expandable && !isExpanded && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: isArray ? `[${entries.length}]` : `{${entries.length}}` })
303
+ ] }),
304
+ expandable && isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-4 border-l border-border/30 pl-2 mt-1 space-y-0.5", children: entries.map(([key, val]) => /* @__PURE__ */ jsxRuntime.jsx(
305
+ DataTreeNode,
306
+ {
307
+ nodeKey: key,
308
+ value: val,
309
+ level: level + 1,
310
+ maxLevel,
311
+ maxDepthLabel
312
+ },
313
+ `${nodeKey}-${key}`
314
+ )) })
315
+ ] });
316
+ }
317
+ function DebugView({
318
+ data,
319
+ className,
320
+ maxDepthLabel = "... (max depth)"
321
+ }) {
322
+ if (data === null || data === void 0) {
323
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("overflow-auto p-4 font-mono text-xs", className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground", children: "null / undefined" }) });
324
+ }
325
+ const isArray = Array.isArray(data);
326
+ const entries = isArray ? data.map((v, i) => [String(i), v]) : typeof data === "object" ? Object.entries(data) : [];
327
+ return /* @__PURE__ */ jsxRuntime.jsx(
328
+ "div",
329
+ {
330
+ className: cn(
331
+ "overflow-auto p-4 font-mono text-xs rounded-lg border bg-muted border-border/30",
332
+ className
333
+ ),
334
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: entries.map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsx(
335
+ DataTreeNode,
336
+ {
337
+ nodeKey: key,
338
+ value,
339
+ level: 0,
340
+ maxDepthLabel
341
+ },
342
+ key
343
+ )) })
344
+ }
345
+ );
346
+ }
347
+
348
+ // src/utils/filterDataByKeys.ts
349
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
350
+ var pathMatchesIncluded = (currentPath, paths) => paths.has(currentPath) || Array.from(paths).some((p) => p.startsWith(`${currentPath}.`));
351
+ var pathMatchesExcluded = (currentPath, paths) => paths.has(currentPath) || Array.from(paths).some(
352
+ (p) => currentPath.startsWith(`${p}.`) || currentPath === p
353
+ );
354
+ function filterObjectRecursive(obj, options, currentPath, depth) {
355
+ const {
356
+ includedKeys,
357
+ excludedKeys,
358
+ includedNestedKeys,
359
+ excludedNestedKeys,
360
+ maxDepth = 20
361
+ } = options;
362
+ const incSet = includedKeys?.length ? new Set(includedKeys) : null;
363
+ const excSet = excludedKeys?.length ? new Set(excludedKeys) : null;
364
+ const incNestedSet = includedNestedKeys?.length ? new Set(includedNestedKeys) : null;
365
+ const excNestedSet = excludedNestedKeys?.length ? new Set(excludedNestedKeys) : null;
366
+ const shouldIncludeTopLevel = (key) => {
367
+ if (incSet) return incSet.has(key);
368
+ if (excSet && excSet.has(key)) return false;
369
+ return true;
370
+ };
371
+ const shouldIncludeNested = (path) => {
372
+ if (incNestedSet && incNestedSet.size > 0)
373
+ return pathMatchesIncluded(path, incNestedSet);
374
+ if (excNestedSet && pathMatchesExcluded(path, excNestedSet)) return false;
375
+ return true;
376
+ };
377
+ const result = {};
378
+ const entries = Object.entries(obj);
379
+ const isTopLevel = !currentPath;
380
+ for (const [key, value] of entries) {
381
+ const childPath = currentPath ? `${currentPath}.${key}` : key;
382
+ if (isTopLevel) {
383
+ if (incNestedSet?.size ? !shouldIncludeNested(childPath) : !shouldIncludeTopLevel(key))
384
+ continue;
385
+ } else if (!shouldIncludeNested(childPath)) {
386
+ continue;
387
+ }
388
+ if (depth >= maxDepth) {
389
+ result[key] = value;
390
+ continue;
391
+ }
392
+ if (Array.isArray(value)) {
393
+ result[key] = value.map(
394
+ (item, i) => isPlainObject(item) ? filterObjectRecursive(item, options, `${childPath}[${i}]`, depth + 1) : item
395
+ );
396
+ } else if (isPlainObject(value)) {
397
+ result[key] = filterObjectRecursive(value, options, childPath, depth + 1);
398
+ } else {
399
+ result[key] = value;
400
+ }
401
+ }
402
+ return result;
403
+ }
404
+ function filterDataByKeys(data, options) {
405
+ if (!options || !options.includedKeys?.length && !options.excludedKeys?.length && !options.includedNestedKeys?.length && !options.excludedNestedKeys?.length) {
406
+ return data;
407
+ }
408
+ if (Array.isArray(data)) {
409
+ return data.map(
410
+ (item, i) => isPlainObject(item) ? filterObjectRecursive(
411
+ item,
412
+ options,
413
+ `[${i}]`,
414
+ 0
415
+ ) : item
416
+ );
417
+ }
418
+ if (isPlainObject(data)) {
419
+ return filterObjectRecursive(
420
+ data,
421
+ options,
422
+ "",
423
+ 0
424
+ );
425
+ }
426
+ return data;
427
+ }
428
+
429
+ // src/i18n/defaultLabels.ts
430
+ var DEFAULT_LABELS = {
431
+ yes: "Yes",
432
+ no: "No",
433
+ shareablePreviewCopySuccess: "Data copied to the clipboard.",
434
+ shareablePreviewShareLinkCopied: "The share link has been copied.",
435
+ shareablePreviewShareUnavailable: "Sharing is unavailable for this preview.",
436
+ shareablePreviewDownloadSuccess: "Data has been downloaded.",
437
+ shareablePreviewClipboardError: "Unable to copy data, please try again.",
438
+ shareablePreviewClipboardUnsupported: "Clipboard copy is not supported on this device.",
439
+ shareablePreviewDownloadUnavailable: "Download is unavailable on this device.",
440
+ shareablePreviewDefaultTitle: "Preview",
441
+ shareablePreviewOpen: "Open preview",
442
+ shareablePreviewDebugMode: "Debug",
443
+ shareablePreviewUserMode: "User",
444
+ shareablePreviewSwitchToUser: "Switch to user view",
445
+ shareablePreviewSwitchToDebug: "Switch to debug view",
446
+ shareablePreviewCopyData: "Copy data",
447
+ shareablePreviewCopyShareLink: "Copy share link",
448
+ shareablePreviewDownloadData: "Download data",
449
+ shareablePreviewCollapseContent: "Collapse content",
450
+ shareablePreviewExpandContent: "Expand content",
451
+ shareablePreviewDataTypeArray: "Array",
452
+ shareablePreviewDataTypeObject: "Object",
453
+ shareablePreviewDataTypeHtml: "HTML",
454
+ shareablePreviewDataTypeText: "Text",
455
+ shareablePreviewDataTypeString: "String",
456
+ shareablePreviewDataTypeNumber: "Number",
457
+ shareablePreviewDataTypeBoolean: "Boolean",
458
+ shareablePreviewDataTypeUndefined: "Undefined",
459
+ shareablePreviewDataTypeFunction: "Function",
460
+ shareablePreviewDataTypeSymbol: "Symbol",
461
+ shareablePreviewDataTypeBigint: "BigInt",
462
+ shareablePreviewEmptyList: "Empty list",
463
+ shareablePreviewEmptyObject: "Empty object",
464
+ shareablePreviewMaxDepth: "Maximum depth reached",
465
+ shareablePreviewItemLabel: "Item {{index}}",
466
+ shareablePreviewEntriesCount: "{{count}} field(s)",
467
+ shareablePreviewArrayLabel: "{{count}} item(s)",
468
+ shareablePreviewNestedObjectLabel: "Nested content",
469
+ shareablePreviewNoValue: "No value",
470
+ shareablePreviewGeneralInfo: "General information"
471
+ };
472
+ function interpolate(template, params) {
473
+ return Object.entries(params).reduce(
474
+ (acc, [key, value]) => acc.replace(`{{${key}}}`, String(value)),
475
+ template
476
+ );
477
+ }
478
+ var MAX_RENDER_DEPTH = 6;
479
+ var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
480
+ var isPrimitive = (value) => ["string", "number", "boolean"].includes(typeof value);
481
+ var formatKey = (key) => key.replace(/[_-]/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/\s+/g, " ").trim();
482
+ var stringifyValue = (value) => {
483
+ if (typeof value === "string") return value;
484
+ try {
485
+ return JSON.stringify(value, null, 2);
486
+ } catch {
487
+ return String(value);
488
+ }
489
+ };
490
+ function UserView({
491
+ data,
492
+ className,
493
+ labels: labelsOverride = {},
494
+ includedKeys,
495
+ excludedKeys,
496
+ includedNestedKeys,
497
+ excludedNestedKeys,
498
+ maxDepth
499
+ }) {
500
+ const displayData = React5.useMemo(() => {
501
+ const opts = {
502
+ includedKeys,
503
+ excludedKeys,
504
+ includedNestedKeys,
505
+ excludedNestedKeys,
506
+ maxDepth
507
+ };
508
+ return filterDataByKeys(data, opts);
509
+ }, [
510
+ data,
511
+ includedKeys,
512
+ excludedKeys,
513
+ includedNestedKeys,
514
+ excludedNestedKeys,
515
+ maxDepth
516
+ ]);
517
+ const labels = {
518
+ yes: labelsOverride.yes ?? "Yes",
519
+ no: labelsOverride.no ?? "No",
520
+ shareablePreviewEmptyList: labelsOverride.shareablePreviewEmptyList ?? "Empty list",
521
+ shareablePreviewEmptyObject: labelsOverride.shareablePreviewEmptyObject ?? "Empty object",
522
+ shareablePreviewNoValue: labelsOverride.shareablePreviewNoValue ?? "No value",
523
+ shareablePreviewItemLabel: labelsOverride.shareablePreviewItemLabel ?? "Item {{index}}",
524
+ shareablePreviewEntriesCount: labelsOverride.shareablePreviewEntriesCount ?? "{{count}} field(s)",
525
+ shareablePreviewArrayLabel: labelsOverride.shareablePreviewArrayLabel ?? "{{count}} item(s)",
526
+ shareablePreviewNestedObjectLabel: labelsOverride.shareablePreviewNestedObjectLabel ?? "Nested content"
527
+ };
528
+ const t = (key, params) => params ? interpolate(labels[key] ?? key, params) : labels[key] ?? key;
529
+ const renderPrimitive = (value) => {
530
+ if (typeof value === "boolean") {
531
+ return /* @__PURE__ */ jsxRuntime.jsx(
532
+ "span",
533
+ {
534
+ className: cn(
535
+ "inline-flex items-center px-3 py-1 text-xs font-semibold tracking-wide uppercase rounded-full",
536
+ value ? "text-emerald-700 bg-emerald-100 dark:bg-emerald-400/20 dark:text-emerald-100" : "bg-slate-100 text-slate-700 dark:bg-slate-400/20 dark:text-slate-100"
537
+ ),
538
+ children: t(value ? "yes" : "no")
539
+ }
540
+ );
541
+ }
542
+ if (typeof value === "number") {
543
+ const safeNum = value == null || Number.isNaN(value) ? "\u2014" : value.toLocaleString();
544
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tracking-tight text-foreground", children: safeNum });
545
+ }
546
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm leading-relaxed whitespace-pre-wrap break-words text-foreground", children: value });
547
+ };
548
+ function renderArray(value, depth, parentKey, inline = false) {
549
+ if (value.length === 0) {
550
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm italic text-muted-foreground", children: t("shareablePreviewEmptyList") });
551
+ }
552
+ if (value.every((item) => isPrimitive(item))) {
553
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
554
+ "span",
555
+ {
556
+ className: "inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/15 px-3 py-1.5 text-xs font-medium text-primary shadow-sm backdrop-blur-sm",
557
+ children: typeof item === "boolean" ? t(item ? "yes" : "no") : typeof item === "number" ? item == null || Number.isNaN(item) ? "\u2014" : item.toLocaleString() : String(item)
558
+ },
559
+ `${parentKey}-primitive-${index}`
560
+ )) });
561
+ }
562
+ const containsStructuredContent = value.some((item) => isPlainObject2(item)) || value.some((item) => Array.isArray(item));
563
+ if (containsStructuredContent) {
564
+ if (inline) {
565
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
566
+ "div",
567
+ {
568
+ className: "px-3 py-2 bg-white rounded-lg ring-1 shadow-sm ring-border/40 shadow-foreground/10 dark:bg-slate-900",
569
+ children: [
570
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-2 text-xs font-semibold tracking-wide uppercase text-muted-foreground", children: [
571
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("shareablePreviewItemLabel", { index: index + 1 }) }),
572
+ isPlainObject2(item) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium lowercase text-muted-foreground", children: t("shareablePreviewEntriesCount", {
573
+ count: Object.keys(
574
+ item
575
+ ).length
576
+ }) })
577
+ ] }),
578
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: renderValue(item, depth + 1, `${parentKey}[${index}]`, true) })
579
+ ]
580
+ },
581
+ `${parentKey}-object-inline-${index}`
582
+ )) });
583
+ }
584
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
585
+ "div",
586
+ {
587
+ className: "bg-white rounded-xl border shadow-xl border-border/40 shadow-foreground/10 dark:bg-slate-900",
588
+ children: [
589
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center px-4 py-2 text-xs font-medium tracking-wide uppercase border-b border-border/40 bg-muted/40 text-muted-foreground", children: [
590
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("shareablePreviewItemLabel", { index: index + 1 }) }),
591
+ isPlainObject2(item) && /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("shareablePreviewEntriesCount", {
592
+ count: Object.keys(
593
+ item
594
+ ).length
595
+ }) })
596
+ ] }),
597
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 space-y-2", children: renderValue(item, depth + 1, `${parentKey}[${index}]`, true) })
598
+ ]
599
+ },
600
+ `${parentKey}-object-${index}`
601
+ )) });
602
+ }
603
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
604
+ "div",
605
+ {
606
+ className: "px-3 py-2 text-xs bg-white rounded-lg border shadow-sm border-border/30 text-muted-foreground shadow-foreground/10 dark:bg-slate-900",
607
+ children: renderValue(item, depth + 1, `${parentKey}[${index}]`, true)
608
+ },
609
+ `${parentKey}-fallback-${index}`
610
+ )) });
611
+ }
612
+ function renderObject(value, depth, parentKey, inline = false) {
613
+ const entries = Object.entries(value);
614
+ if (entries.length === 0) {
615
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm italic text-muted-foreground", children: t("shareablePreviewEmptyObject") });
616
+ }
617
+ if (inline) {
618
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: entries.map(([key, val]) => /* @__PURE__ */ jsxRuntime.jsxs(
619
+ "div",
620
+ {
621
+ className: "px-3 py-2 bg-white rounded-lg ring-1 shadow-sm ring-border/30 shadow-foreground/10 dark:bg-slate-900/90",
622
+ children: [
623
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold tracking-wide uppercase text-muted-foreground", children: formatKey(key) }),
624
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-sm text-foreground", children: renderValue(val, depth + 1, `${parentKey}.${key}`, true) })
625
+ ]
626
+ },
627
+ `${parentKey}-${key}`
628
+ )) });
629
+ }
630
+ const gridClass = depth === 0 ? "grid gap-4 md:grid-cols-2" : depth === 1 ? "grid gap-3 md:grid-cols-1" : "space-y-3";
631
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: gridClass, children: entries.map(([key, val]) => /* @__PURE__ */ jsxRuntime.jsxs(
632
+ "div",
633
+ {
634
+ className: cn(
635
+ "bg-white rounded-xl border shadow-xl border-border/30 shadow-foreground/10 dark:bg-slate-900"
636
+ ),
637
+ children: [
638
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-between items-center px-4 py-2 border-b border-border/30 bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold tracking-wide uppercase text-muted-foreground", children: formatKey(key) }) }),
639
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 space-y-2", children: renderValue(val, depth + 1, `${parentKey}.${key}`, true) })
640
+ ]
641
+ },
642
+ `${parentKey}-${key}`
643
+ )) });
644
+ }
645
+ function renderValue(value, depth, parentKey, inline = false) {
646
+ if (depth > MAX_RENDER_DEPTH) {
647
+ return /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-auto px-3 py-2 max-h-48 text-xs bg-white rounded-md border shadow-inner border-border/30 text-muted-foreground shadow-foreground/5 dark:bg-slate-900", children: stringifyValue(value) });
648
+ }
649
+ if (value === null || value === void 0) {
650
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm italic text-muted-foreground", children: t("shareablePreviewNoValue") });
651
+ }
652
+ if (isPrimitive(value)) {
653
+ return renderPrimitive(value);
654
+ }
655
+ if (Array.isArray(value)) {
656
+ return renderArray(value, depth, parentKey, inline);
657
+ }
658
+ if (isPlainObject2(value)) {
659
+ return renderObject(value, depth, parentKey, inline);
660
+ }
661
+ return /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "px-3 py-2 text-xs bg-white rounded-md border shadow-inner border-border/30 text-muted-foreground shadow-foreground/5 dark:bg-slate-900", children: stringifyValue(value) });
662
+ }
663
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-4", className), children: renderValue(displayData, 0, "root") });
664
+ }
665
+ var getDataType = (value) => {
666
+ if (value === null) return "null";
667
+ if (value === void 0) return "undefined";
668
+ if (typeof value === "string") {
669
+ return value.startsWith("<") && value.includes(">") ? "HTML" : "Text";
670
+ }
671
+ if (Array.isArray(value)) return "Array";
672
+ if (typeof value === "object") return "Object";
673
+ return typeof value;
674
+ };
675
+ var sanitizeFileName = (value) => value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)+/g, "").concat(".txt");
676
+ var normalizeActionKey = (value) => value ? value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim() : "";
677
+ var ACTION_ICON_MAP = {
678
+ modifier: lucideReact.Pencil,
679
+ edit: lucideReact.Pencil,
680
+ update: lucideReact.Pencil,
681
+ "mise a jour": lucideReact.Pencil,
682
+ maj: lucideReact.Pencil,
683
+ supprimer: lucideReact.Trash2,
684
+ delete: lucideReact.Trash2,
685
+ remove: lucideReact.Trash2,
686
+ retirer: lucideReact.Trash2,
687
+ destroy: lucideReact.Trash2,
688
+ effacer: lucideReact.Trash2,
689
+ partager: lucideReact.Share2,
690
+ share: lucideReact.Share2,
691
+ envoyer: lucideReact.Share2,
692
+ telecharger: lucideReact.Download,
693
+ download: lucideReact.Download,
694
+ exporter: lucideReact.Download,
695
+ voir: lucideReact.Eye,
696
+ afficher: lucideReact.Eye,
697
+ visualiser: lucideReact.Eye,
698
+ preview: lucideReact.Eye,
699
+ annuler: lucideReact.XCircle,
700
+ cancel: lucideReact.XCircle,
701
+ retour: lucideReact.XCircle,
702
+ back: lucideReact.XCircle,
703
+ confirmer: lucideReact.CheckCircle,
704
+ valider: lucideReact.CheckCircle,
705
+ confirm: lucideReact.CheckCircle,
706
+ validate: lucideReact.CheckCircle
707
+ };
708
+ var DANGER_KEYWORDS = [
709
+ "supprimer",
710
+ "delete",
711
+ "remove",
712
+ "retirer",
713
+ "destroy",
714
+ "effacer",
715
+ "annuler",
716
+ "cancel",
717
+ "retour",
718
+ "abort",
719
+ "stop"
720
+ ];
721
+ var SUCCESS_KEYWORDS = [
722
+ "confirmer",
723
+ "valider",
724
+ "confirm",
725
+ "validate",
726
+ "approve",
727
+ "approuver"
728
+ ];
729
+ var DEFAULT_ACTION_VARIANT = "blue";
730
+ var findIconByKeyword = (key) => {
731
+ if (!key) return void 0;
732
+ if (ACTION_ICON_MAP[key]) return ACTION_ICON_MAP[key];
733
+ const entry = Object.entries(ACTION_ICON_MAP).find(
734
+ ([keyword]) => key.includes(keyword)
735
+ );
736
+ return entry?.[1];
737
+ };
738
+ function getDefaultEyeDevMode() {
739
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
740
+ return "development";
741
+ }
742
+ return "production";
743
+ }
744
+ function ShareablePreview({
745
+ data,
746
+ title,
747
+ description,
748
+ actions = [],
749
+ shareUrl,
750
+ className,
751
+ maxHeight = "500px",
752
+ mode = "debug",
753
+ expandByDefault = true,
754
+ trigger,
755
+ triggerLabel,
756
+ triggerClassName,
757
+ defaultOpen = false,
758
+ onOpenChange,
759
+ eyeDevMode = getDefaultEyeDevMode(),
760
+ customView,
761
+ customViewProps = {},
762
+ customViews,
763
+ inline = false,
764
+ labels: labelsOverride = {},
765
+ onNotify
766
+ }) {
767
+ const mergedLabels = React5.useMemo(
768
+ () => ({ ...DEFAULT_LABELS, ...labelsOverride }),
769
+ [labelsOverride]
770
+ );
771
+ const t = (key) => mergedLabels[key] ?? key;
772
+ const notify = (type, key) => {
773
+ if (onNotify) {
774
+ onNotify(type, key);
775
+ }
776
+ };
777
+ const [isExpanded, setIsExpanded] = React5.useState(expandByDefault);
778
+ const [currentMode, setCurrentMode] = React5.useState(mode);
779
+ const [isDialogOpen, setIsDialogOpen] = React5.useState(defaultOpen);
780
+ const hasCustomViews = customViews && customViews.length > 0;
781
+ const firstViewId = customViews?.[0]?.id;
782
+ const [activeViewId, setActiveViewId] = React5.useState(firstViewId ?? "");
783
+ React5.useEffect(() => {
784
+ if (hasCustomViews && firstViewId) setActiveViewId(firstViewId);
785
+ }, [hasCustomViews, firstViewId]);
786
+ const effectiveActions = React5.useMemo(() => {
787
+ const list = hasCustomViews && customViews.find((vc) => vc.id === activeViewId)?.actions?.length ? customViews.find((vc) => vc.id === activeViewId).actions : actions;
788
+ return [...list].sort(
789
+ (a, b) => (a.order ?? 999) - (b.order ?? 999)
790
+ );
791
+ }, [hasCustomViews, customViews, activeViewId, actions]);
792
+ React5.useEffect(() => {
793
+ setIsExpanded(expandByDefault);
794
+ }, [expandByDefault]);
795
+ React5.useEffect(() => {
796
+ setCurrentMode(mode);
797
+ }, [mode]);
798
+ React5.useEffect(() => {
799
+ setIsDialogOpen(defaultOpen);
800
+ }, [defaultOpen]);
801
+ React5.useEffect(() => {
802
+ if (!isDialogOpen && !inline) {
803
+ setIsExpanded(expandByDefault);
804
+ setCurrentMode(mode);
805
+ }
806
+ }, [expandByDefault, isDialogOpen, mode, inline]);
807
+ const formatData = React5.useMemo(
808
+ () => (value) => {
809
+ if (value === null || value === void 0) {
810
+ return String(value);
811
+ }
812
+ if (typeof value === "string") {
813
+ try {
814
+ const parsed = JSON.parse(value);
815
+ return JSON.stringify(parsed, null, 2);
816
+ } catch {
817
+ return value;
818
+ }
819
+ }
820
+ if (typeof value === "object") {
821
+ try {
822
+ return JSON.stringify(value, null, 2);
823
+ } catch {
824
+ return String(value);
825
+ }
826
+ }
827
+ return String(value);
828
+ },
829
+ []
830
+ );
831
+ const dataType = React5.useMemo(() => getDataType(data), [data]);
832
+ const dataTypeLabel = React5.useMemo(() => {
833
+ const labels = {
834
+ Array: t("shareablePreviewDataTypeArray"),
835
+ Object: t("shareablePreviewDataTypeObject"),
836
+ HTML: t("shareablePreviewDataTypeHtml"),
837
+ Text: t("shareablePreviewDataTypeText"),
838
+ string: t("shareablePreviewDataTypeString"),
839
+ number: t("shareablePreviewDataTypeNumber"),
840
+ boolean: t("shareablePreviewDataTypeBoolean"),
841
+ undefined: t("shareablePreviewDataTypeUndefined"),
842
+ function: t("shareablePreviewDataTypeFunction"),
843
+ symbol: t("shareablePreviewDataTypeSymbol"),
844
+ bigint: t("shareablePreviewDataTypeBigint")
845
+ };
846
+ return labels[dataType] ?? dataType;
847
+ }, [dataType, t]);
848
+ const handleDialogOpenChange = (nextOpen) => {
849
+ setIsDialogOpen(nextOpen);
850
+ onOpenChange?.(nextOpen);
851
+ };
852
+ const handleCopy = async () => {
853
+ if (!("clipboard" in navigator) || !navigator.clipboard?.writeText) {
854
+ notify("error", "shareablePreviewClipboardUnsupported");
855
+ return;
856
+ }
857
+ try {
858
+ const textToCopy = formatData(data);
859
+ await navigator.clipboard.writeText(textToCopy);
860
+ notify("success", "shareablePreviewCopySuccess");
861
+ } catch {
862
+ notify("error", "shareablePreviewClipboardError");
863
+ }
864
+ };
865
+ const handleShare = async () => {
866
+ if (!("clipboard" in navigator) || !navigator.clipboard?.writeText) {
867
+ notify("error", "shareablePreviewClipboardUnsupported");
868
+ return;
869
+ }
870
+ try {
871
+ if (shareUrl) {
872
+ await navigator.clipboard.writeText(shareUrl);
873
+ notify("success", "shareablePreviewShareLinkCopied");
874
+ } else {
875
+ notify("error", "shareablePreviewShareUnavailable");
876
+ }
877
+ } catch {
878
+ notify("error", "shareablePreviewClipboardError");
879
+ }
880
+ };
881
+ const handleDownload = () => {
882
+ const content = formatData(data);
883
+ if (typeof document === "undefined") {
884
+ notify("error", "shareablePreviewDownloadUnavailable");
885
+ return;
886
+ }
887
+ const element = document.createElement("a");
888
+ element.setAttribute(
889
+ "href",
890
+ `data:text/plain;charset=utf-8,${encodeURIComponent(content)}`
891
+ );
892
+ element.setAttribute(
893
+ "download",
894
+ sanitizeFileName(
895
+ title && title.trim().length > 0 ? title : t("shareablePreviewDefaultTitle")
896
+ )
897
+ );
898
+ element.style.display = "none";
899
+ document.body.appendChild(element);
900
+ element.click();
901
+ document.body.removeChild(element);
902
+ notify("success", "shareablePreviewDownloadSuccess");
903
+ };
904
+ const computedTitle = title && title.trim().length > 0 ? title : t("shareablePreviewDefaultTitle");
905
+ const dialogTrigger = trigger ?? /* @__PURE__ */ jsxRuntime.jsxs(
906
+ Button,
907
+ {
908
+ variant: "outline",
909
+ size: "sm",
910
+ className: cn(
911
+ "flex items-center gap-2 rounded-full border-primary/40 bg-background/80 px-4 py-2 text-sm font-medium text-primary shadow-sm transition-all hover:-translate-y-0.5 hover:border-primary hover:bg-background",
912
+ triggerClassName
913
+ ),
914
+ children: [
915
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "w-4 h-4" }),
916
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: triggerLabel ?? t("shareablePreviewOpen") })
917
+ ]
918
+ }
919
+ );
920
+ const shouldShowTrigger = trigger !== void 0 || !onOpenChange && !defaultOpen;
921
+ const userViewLabels = {
922
+ yes: mergedLabels.yes,
923
+ no: mergedLabels.no,
924
+ shareablePreviewEmptyList: mergedLabels.shareablePreviewEmptyList,
925
+ shareablePreviewEmptyObject: mergedLabels.shareablePreviewEmptyObject,
926
+ shareablePreviewNoValue: mergedLabels.shareablePreviewNoValue,
927
+ shareablePreviewItemLabel: mergedLabels.shareablePreviewItemLabel,
928
+ shareablePreviewEntriesCount: mergedLabels.shareablePreviewEntriesCount,
929
+ shareablePreviewArrayLabel: mergedLabels.shareablePreviewArrayLabel,
930
+ shareablePreviewNestedObjectLabel: mergedLabels.shareablePreviewNestedObjectLabel
931
+ };
932
+ const previewContent = /* @__PURE__ */ jsxRuntime.jsxs(
933
+ Card,
934
+ {
935
+ className: cn(
936
+ "flex overflow-hidden flex-col border shadow-2xl transition-shadow max-h-[90vh] border-border/70 bg-slate-50 shadow-foreground/10 dark:bg-slate-900",
937
+ className,
938
+ inline && "shadow-sm max-h-none h-full"
939
+ ),
940
+ children: [
941
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-border/50 bg-slate-100 sm:px-6 sm:py-4 dark:bg-slate-900/90", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 justify-between items-start sm:flex-row sm:items-center", children: [
942
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
943
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
944
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold truncate text-foreground", children: computedTitle }),
945
+ eyeDevMode === "development" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap rounded-full bg-primary/20 px-2 py-0.5 text-xs font-mono text-primary", children: dataTypeLabel }),
946
+ eyeDevMode === "development" && /* @__PURE__ */ jsxRuntime.jsx(
947
+ "span",
948
+ {
949
+ className: cn(
950
+ "whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-mono font-medium",
951
+ currentMode === "debug" ? "bg-orange-500/20 text-orange-600" : "bg-blue-500/20 text-blue-600"
952
+ ),
953
+ children: currentMode === "debug" ? t("shareablePreviewDebugMode") : t("shareablePreviewUserMode")
954
+ }
955
+ )
956
+ ] }),
957
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground sm:text-sm", children: description })
958
+ ] }),
959
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [
960
+ eyeDevMode === "development" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
961
+ /* @__PURE__ */ jsxRuntime.jsx(
962
+ Button,
963
+ {
964
+ variant: "ghost",
965
+ size: "sm",
966
+ onClick: () => setCurrentMode(currentMode === "debug" ? "user" : "debug"),
967
+ title: currentMode === "debug" ? t("shareablePreviewSwitchToUser") : t("shareablePreviewSwitchToDebug"),
968
+ className: cn(
969
+ currentMode === "debug" ? "text-orange-600" : "text-blue-600"
970
+ ),
971
+ children: currentMode === "debug" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
972
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bug, { className: "w-4 h-4" }),
973
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden ml-1 text-xs sm:inline", children: t("shareablePreviewDebugMode") })
974
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
975
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code, { className: "w-4 h-4" }),
976
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden ml-1 text-xs sm:inline", children: t("shareablePreviewUserMode") })
977
+ ] })
978
+ }
979
+ ),
980
+ /* @__PURE__ */ jsxRuntime.jsx(
981
+ Button,
982
+ {
983
+ variant: "ghost",
984
+ size: "sm",
985
+ onClick: handleCopy,
986
+ title: t("shareablePreviewCopyData"),
987
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "w-4 h-4" })
988
+ }
989
+ )
990
+ ] }),
991
+ shareUrl && /* @__PURE__ */ jsxRuntime.jsx(
992
+ Button,
993
+ {
994
+ variant: "ghost",
995
+ size: "sm",
996
+ onClick: handleShare,
997
+ title: t("shareablePreviewCopyShareLink"),
998
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Share2, { className: "w-4 h-4" })
999
+ }
1000
+ ),
1001
+ eyeDevMode === "development" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1002
+ /* @__PURE__ */ jsxRuntime.jsx(
1003
+ Button,
1004
+ {
1005
+ variant: "ghost",
1006
+ size: "sm",
1007
+ onClick: handleDownload,
1008
+ title: t("shareablePreviewDownloadData"),
1009
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "w-4 h-4" })
1010
+ }
1011
+ ),
1012
+ /* @__PURE__ */ jsxRuntime.jsx(
1013
+ Button,
1014
+ {
1015
+ variant: "ghost",
1016
+ size: "sm",
1017
+ onClick: () => setIsExpanded((prev) => !prev),
1018
+ className: "ml-auto sm:ml-0",
1019
+ title: isExpanded ? t("shareablePreviewCollapseContent") : t("shareablePreviewExpandContent"),
1020
+ children: isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-4 h-4" })
1021
+ }
1022
+ )
1023
+ ] })
1024
+ ] })
1025
+ ] }) }),
1026
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsxs(
1027
+ "div",
1028
+ {
1029
+ className: "overflow-auto flex-1 p-4 bg-slate-50 sm:p-6 dark:bg-slate-900",
1030
+ style: { maxHeight: inline ? void 0 : maxHeight },
1031
+ children: [
1032
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(currentMode === "debug" ? "block" : "hidden"), children: /* @__PURE__ */ jsxRuntime.jsx(
1033
+ DebugView,
1034
+ {
1035
+ data,
1036
+ className: "overflow-visible p-0 text-xs bg-transparent border-none",
1037
+ maxDepthLabel: mergedLabels.shareablePreviewMaxDepth
1038
+ }
1039
+ ) }),
1040
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(currentMode === "user" ? "block" : "hidden"), children: hasCustomViews ? /* @__PURE__ */ jsxRuntime.jsxs(
1041
+ Tabs,
1042
+ {
1043
+ value: activeViewId,
1044
+ onValueChange: setActiveViewId,
1045
+ className: "w-full",
1046
+ children: [
1047
+ /* @__PURE__ */ jsxRuntime.jsx(TabsList, { className: "mb-4 w-full justify-start overflow-x-auto", children: customViews.map((vc) => /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: vc.id, children: vc.label }, vc.id)) }),
1048
+ customViews.map((vc) => /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: vc.id, className: "mt-0", children: React5__namespace.default.createElement(vc.view, {
1049
+ data,
1050
+ expandByDefault,
1051
+ ...vc.viewProps
1052
+ }) }, vc.id))
1053
+ ]
1054
+ }
1055
+ ) : customView ? React5__namespace.default.createElement(customView, {
1056
+ data,
1057
+ expandByDefault,
1058
+ ...customViewProps
1059
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
1060
+ UserView,
1061
+ {
1062
+ data,
1063
+ expandByDefault,
1064
+ labels: userViewLabels,
1065
+ ...customViewProps
1066
+ }
1067
+ ) })
1068
+ ]
1069
+ }
1070
+ ),
1071
+ effectiveActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-t border-border/50 bg-slate-100 sm:px-6 dark:bg-slate-900/90", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3", children: effectiveActions.map((action, idx) => {
1072
+ const {
1073
+ label,
1074
+ icon,
1075
+ onClick,
1076
+ variant,
1077
+ withPulse,
1078
+ withIndicator,
1079
+ buttonClassName,
1080
+ disabled
1081
+ } = action;
1082
+ const normalizedLabel = normalizeActionKey(label);
1083
+ const normalizedIconHint = typeof icon === "string" ? normalizeActionKey(icon) : "";
1084
+ const isDangerAction = DANGER_KEYWORDS.some(
1085
+ (keyword) => normalizedLabel.includes(keyword) || normalizedIconHint.includes(keyword)
1086
+ );
1087
+ const isSuccessAction = SUCCESS_KEYWORDS.some(
1088
+ (keyword) => normalizedLabel.includes(keyword) || normalizedIconHint.includes(keyword)
1089
+ );
1090
+ const resolvedVariant = variant ?? (isDangerAction ? "red" : isSuccessAction ? "green" : DEFAULT_ACTION_VARIANT);
1091
+ let resolvedIcon;
1092
+ if (typeof icon === "function") {
1093
+ resolvedIcon = icon;
1094
+ } else if (React5__namespace.default.isValidElement(icon)) {
1095
+ const elementWithClassName = icon;
1096
+ resolvedIcon = ({ className: cls }) => React5__namespace.default.cloneElement(elementWithClassName, {
1097
+ className: cn(
1098
+ "w-4 h-4",
1099
+ cls,
1100
+ elementWithClassName.props.className
1101
+ )
1102
+ });
1103
+ } else {
1104
+ resolvedIcon = findIconByKeyword(normalizedIconHint) ?? findIconByKeyword(normalizedLabel);
1105
+ }
1106
+ return /* @__PURE__ */ jsxRuntime.jsx(
1107
+ ValidationButton,
1108
+ {
1109
+ onClick,
1110
+ disabled,
1111
+ icon: resolvedIcon,
1112
+ variant: resolvedVariant,
1113
+ withPulse: withPulse ?? false,
1114
+ withIndicator: withIndicator ?? false,
1115
+ className: cn(
1116
+ "justify-center px-5 py-2 text-sm font-semibold tracking-wide uppercase min-w-[150px]",
1117
+ buttonClassName,
1118
+ isDangerAction ? "shadow-red-400/30" : isSuccessAction ? "shadow-green-400/30" : "shadow-primary/20"
1119
+ ),
1120
+ children: label
1121
+ },
1122
+ `${label}-${idx}`
1123
+ );
1124
+ }) }) })
1125
+ ]
1126
+ }
1127
+ );
1128
+ if (inline) {
1129
+ return previewContent;
1130
+ }
1131
+ return /* @__PURE__ */ jsxRuntime.jsxs(Dialog, { open: isDialogOpen, onOpenChange: handleDialogOpenChange, children: [
1132
+ shouldShowTrigger && /* @__PURE__ */ jsxRuntime.jsx(DialogTrigger, { asChild: true, children: dialogTrigger }),
1133
+ /* @__PURE__ */ jsxRuntime.jsx(DialogContent, { className: "w-full max-w-[95vw] overflow-hidden border-none bg-transparent p-0 sm:max-w-4xl lg:max-w-5xl xl:max-w-6xl", children: previewContent })
1134
+ ] });
1135
+ }
1136
+ function createPreviewActions(options) {
1137
+ const {
1138
+ onConfirm,
1139
+ onReject,
1140
+ onCancel,
1141
+ labels = {},
1142
+ withIndicatorOnConfirm = true,
1143
+ withPulseOnConfirm = false,
1144
+ order = "confirm-first"
1145
+ } = options;
1146
+ const confirmLabel = labels.confirm ?? "Confirmer";
1147
+ const rejectLabel = labels.reject ?? "Rejeter";
1148
+ const cancelLabel = labels.cancel ?? "Annuler";
1149
+ const base = order === "cancel-first" ? { cancel: 0, confirm: 1, reject: 2 } : { confirm: 1, reject: 2, cancel: 3 };
1150
+ const confirmAction = {
1151
+ label: confirmLabel,
1152
+ icon: lucideReact.CheckCircle,
1153
+ variant: "green",
1154
+ onClick: onConfirm,
1155
+ withIndicator: withIndicatorOnConfirm,
1156
+ withPulse: withPulseOnConfirm,
1157
+ order: base.confirm
1158
+ };
1159
+ const rejectAction = onReject ? {
1160
+ label: rejectLabel,
1161
+ icon: lucideReact.XCircle,
1162
+ variant: "red",
1163
+ onClick: onReject,
1164
+ order: base.reject
1165
+ } : null;
1166
+ const cancelAction = onCancel ? {
1167
+ label: cancelLabel,
1168
+ icon: lucideReact.XCircle,
1169
+ variant: "blue",
1170
+ onClick: onCancel,
1171
+ order: base.cancel
1172
+ } : null;
1173
+ return [confirmAction, rejectAction, cancelAction].filter(
1174
+ (a) => a !== null
1175
+ );
1176
+ }
1177
+
1178
+ exports.DEFAULT_LABELS = DEFAULT_LABELS;
1179
+ exports.DataTreeNode = DataTreeNode;
1180
+ exports.DebugView = DebugView;
1181
+ exports.ShareablePreview = ShareablePreview;
1182
+ exports.UserView = UserView;
1183
+ exports.createPreviewActions = createPreviewActions;
1184
+ exports.filterDataByKeys = filterDataByKeys;