@wipco/sdui 1.9.0 → 1.9.2

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 (84) hide show
  1. package/dist/{chunk-SMVPMDWO.js → chunk-35GI6LM5.js} +2 -2
  2. package/dist/{chunk-J2SPMIMO.js → chunk-3L4EXK3X.js} +2 -2
  3. package/dist/{chunk-VSCAW4LS.js → chunk-3XUHKSJM.js} +1 -1
  4. package/dist/{chunk-NKRZVRGH.js → chunk-45GIMFRP.js} +4 -2
  5. package/dist/{chunk-3M64LBYZ.js → chunk-5DLUGN5M.js} +2 -2
  6. package/dist/{chunk-55524GIB.js → chunk-5N4ESLPX.js} +2 -2
  7. package/dist/{chunk-RT2I2U45.js → chunk-77OM6OQG.js} +1 -1
  8. package/dist/{chunk-UYOQ3SEG.js → chunk-BBA7KKG4.js} +1 -1
  9. package/dist/{chunk-NHKQZDBX.js → chunk-BOL536MN.js} +2 -2
  10. package/dist/{chunk-TYPNHH3I.js → chunk-BT5P3DW6.js} +1 -1
  11. package/dist/{chunk-ZHWBTWHS.js → chunk-CBY6ACMR.js} +2 -2
  12. package/dist/{chunk-IOQNDAWT.js → chunk-CKZ66V5Q.js} +13 -156
  13. package/dist/{chunk-LK7MHTJO.js → chunk-CRSNVEUE.js} +2 -2
  14. package/dist/{chunk-RMYGRGKS.js → chunk-DE7FPWVJ.js} +1 -1
  15. package/dist/{chunk-SANCYRIQ.js → chunk-EH5QJJQE.js} +1 -1
  16. package/dist/{chunk-3LPLTD66.js → chunk-EKNEBGNW.js} +245 -8
  17. package/dist/chunk-FLBU7AVR.js +130 -0
  18. package/dist/{chunk-I45MRYCE.js → chunk-FVONSLW3.js} +2 -2
  19. package/dist/{chunk-Z2SOMUZF.js → chunk-GAABHPGR.js} +1 -1
  20. package/dist/{chunk-33LRJ25P.js → chunk-GLAG6ZT3.js} +152 -123
  21. package/dist/{chunk-FR7S52ZL.js → chunk-HNMBEUBO.js} +1 -1
  22. package/dist/{chunk-A2PG44CM.js → chunk-HR2U2QGN.js} +1 -1
  23. package/dist/{chunk-LDIEWJNP.js → chunk-KMNC3Q4J.js} +2 -2
  24. package/dist/{chunk-GHKRT7EA.js → chunk-LRDGT4T4.js} +1 -1
  25. package/dist/{chunk-LEYVJAXU.js → chunk-M4CZZMZJ.js} +3 -3
  26. package/dist/{chunk-4FQB6O4T.js → chunk-MBROAHA6.js} +5 -5
  27. package/dist/{chunk-GW2LSUA4.js → chunk-MLWNI4HI.js} +1 -1
  28. package/dist/{chunk-HKYLKLF6.js → chunk-MY7VBYHI.js} +2 -2
  29. package/dist/{chunk-CZMSQ3B3.js → chunk-PPOQY3V5.js} +1 -1
  30. package/dist/{chunk-VGNLXT3A.js → chunk-Q6V7E2H5.js} +1 -1
  31. package/dist/{chunk-MIFCOTLQ.js → chunk-QDCP6P3K.js} +2 -2
  32. package/dist/{chunk-UX37MB4P.js → chunk-QM2YJS7J.js} +3 -3
  33. package/dist/{chunk-Z5Q44VXX.js → chunk-QZMMW3JC.js} +1 -1
  34. package/dist/{chunk-GUXKGL76.js → chunk-T5ZHEXGK.js} +1 -1
  35. package/dist/{chunk-DELAII7K.js → chunk-TVJS5JAK.js} +2 -2
  36. package/dist/{chunk-LNEFMVHL.js → chunk-VGWOOPHH.js} +3 -3
  37. package/dist/{chunk-F2ICWCV3.js → chunk-WIQIJDEC.js} +1 -1
  38. package/dist/{chunk-MRCTYYBS.js → chunk-WS3QZIYN.js} +2 -2
  39. package/dist/{chunk-YMJUK3V4.js → chunk-WZS7KYOS.js} +2 -2
  40. package/dist/{chunk-JHWJSVZH.js → chunk-YSRJPBMO.js} +2 -2
  41. package/dist/{chunk-DE3HYL4K.js → chunk-ZAESY6NX.js} +2 -2
  42. package/dist/{chunk-NTMFLJEE.js → chunk-ZSKQUIYR.js} +4 -4
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.js +17 -17
  45. package/dist/meta.js +57 -3
  46. package/dist/rich/ExcelUploadCard.js +3 -3
  47. package/dist/rich.js +5 -5
  48. package/dist/widgets/ApprovalLineEditor.js +7 -7
  49. package/dist/widgets/ApprovalOverridesEditor.js +9 -9
  50. package/dist/widgets/ApprovalStepper.js +2 -2
  51. package/dist/widgets/AutocompleteField.js +7 -7
  52. package/dist/widgets/ColumnBoard.js +2 -2
  53. package/dist/widgets/DiffView.js +2 -2
  54. package/dist/widgets/Disclosure.js +2 -2
  55. package/dist/widgets/EventCalendar.js +3 -3
  56. package/dist/widgets/FileDropzone.d.ts +1 -0
  57. package/dist/widgets/FileDropzone.js +5 -5
  58. package/dist/widgets/ImageLightbox.d.ts +5 -0
  59. package/dist/widgets/ImageLightbox.js +4 -3
  60. package/dist/widgets/InlineEdit.js +2 -2
  61. package/dist/widgets/JsonField.js +2 -2
  62. package/dist/widgets/OptionSchemaEditor.js +3 -3
  63. package/dist/widgets/OptionValueFields.js +3 -3
  64. package/dist/widgets/OrgChart.js +2 -2
  65. package/dist/widgets/OrgChartBoard.js +5 -5
  66. package/dist/widgets/PeriodFilter.js +4 -4
  67. package/dist/widgets/PermissionMatrixEditor.js +3 -3
  68. package/dist/widgets/PositionSelect.js +3 -3
  69. package/dist/widgets/PrintSheet.js +2 -2
  70. package/dist/widgets/ProfileEditDiffCard.js +3 -3
  71. package/dist/widgets/QRDisplay.js +3 -3
  72. package/dist/widgets/RankBoard.js +4 -4
  73. package/dist/widgets/RoleMemberPicker.js +4 -4
  74. package/dist/widgets/SearchableMultiPicker.js +5 -5
  75. package/dist/widgets/SecureScreen.js +2 -2
  76. package/dist/widgets/StagePeriodEditor.js +4 -4
  77. package/dist/widgets/TagInput.js +2 -2
  78. package/dist/widgets/TagMultiSelect.js +3 -3
  79. package/dist/widgets/Timeline.js +3 -3
  80. package/dist/widgets/TreePicker.js +6 -6
  81. package/dist/widgets/TreeView.js +5 -5
  82. package/dist/widgets/index.js +71 -71
  83. package/package.json +1 -1
  84. package/dist/chunk-5NDYDUMJ.js +0 -73
@@ -1,6 +1,6 @@
1
- import { includesLooseSearch } from './chunk-MIFCOTLQ.js';
1
+ import { includesLooseSearch } from './chunk-QDCP6P3K.js';
2
2
  import { OverlayLayer } from './chunk-R2ORCOTG.js';
3
- import { Button, Dialog, Input, Tag, Checkbox } from './chunk-NKRZVRGH.js';
3
+ import { Button, Dialog, Input, Tag, Checkbox } from './chunk-45GIMFRP.js';
4
4
  import { boxed } from './chunk-3JTBOABA.js';
5
5
  import React, { useMemo, useState } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { Disclosure } from './chunk-UYOQ3SEG.js';
2
- import { Icon } from './chunk-NKRZVRGH.js';
1
+ import { Disclosure } from './chunk-BBA7KKG4.js';
2
+ import { Icon } from './chunk-45GIMFRP.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
 
@@ -1,4 +1,4 @@
1
- import { Button } from './chunk-NKRZVRGH.js';
1
+ import { Button } from './chunk-45GIMFRP.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useEffect, useState } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1282,8 +1282,10 @@ function sanitizeNumberText(text) {
1282
1282
  let dot = false;
1283
1283
  for (const raw of text) {
1284
1284
  const ch = halfWidth(raw);
1285
- if (ch >= "0" && ch <= "9") out += ch;
1286
- else if (ch === "." && !dot) {
1285
+ if (ch >= "0" && ch <= "9") {
1286
+ if (!dot && (out === "0" || out === "-0")) out = out.slice(0, -1);
1287
+ out += ch;
1288
+ } else if (ch === "." && !dot) {
1287
1289
  dot = true;
1288
1290
  out += ch;
1289
1291
  } else if (ch === "-" && out === "") out += ch;
@@ -1,5 +1,5 @@
1
- import { useSduiAdapter, hostFetchOf } from './chunk-RMYGRGKS.js';
2
- import { Badge, Tag } from './chunk-NKRZVRGH.js';
1
+ import { useSduiAdapter, hostFetchOf } from './chunk-DE7FPWVJ.js';
2
+ import { Badge, Tag } from './chunk-45GIMFRP.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { useRef, useEffect, useMemo, useState } from 'react';
5
5
  import { jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { Text } from './chunk-CZMSQ3B3.js';
2
- import { Switch, Field, Select, Input } from './chunk-NKRZVRGH.js';
1
+ import { Text } from './chunk-PPOQY3V5.js';
2
+ import { Switch, Field, Select, Input } from './chunk-45GIMFRP.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import React, { useMemo } from 'react';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
1
  import { OverlayLayer } from './chunk-R2ORCOTG.js';
2
- import { Dialog, Button, warnOnce } from './chunk-NKRZVRGH.js';
2
+ import { Dialog, Button, warnOnce } from './chunk-45GIMFRP.js';
3
3
  import { createContext, useContext, useMemo, useRef, useCallback, useState } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -1,4 +1,4 @@
1
- import { Icon } from './chunk-NKRZVRGH.js';
1
+ import { Icon } from './chunk-45GIMFRP.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useState, useId, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,7 +1,7 @@
1
1
  import { HoistedStyle } from './chunk-RIZRM7OO.js';
2
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-RMYGRGKS.js';
2
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DE7FPWVJ.js';
3
3
  import { MOBILE_MEDIA_QUERY } from './chunk-TAVKGYH5.js';
4
- import { Icon, Badge, Checkbox, Select, Button } from './chunk-NKRZVRGH.js';
4
+ import { Icon, Badge, Checkbox, Select, Button } from './chunk-45GIMFRP.js';
5
5
  import { boxed } from './chunk-3JTBOABA.js';
6
6
  import { useRef, useEffect, useState, useMemo } from 'react';
7
7
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { Textarea, Icon } from './chunk-NKRZVRGH.js';
1
+ import { Textarea, Icon } from './chunk-45GIMFRP.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useState, useRef, useId, useEffect, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-RMYGRGKS.js';
2
- import { Spinner, Badge, Icon } from './chunk-NKRZVRGH.js';
1
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DE7FPWVJ.js';
2
+ import { Spinner, Badge, Icon } from './chunk-45GIMFRP.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { useState, useEffect } from 'react';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,96 +1,12 @@
1
- import { ImageLightbox } from './chunk-5NDYDUMJ.js';
2
- import { useSduiSandbox, useSduiAdapter, hostRawFetchOf, defaultAdapter, useSduiAssetResolver } from './chunk-RMYGRGKS.js';
3
- import { Spinner, Icon, IconButton } from './chunk-NKRZVRGH.js';
1
+ import { ImageLightbox } from './chunk-FLBU7AVR.js';
2
+ import { looksLikeImage, fileMimeOf, FileTile, fileBadgeOf, fileDisplayNameOf, safeFileHref, FILE_TILE_WIDTH, IMAGE_EXTENSIONS } from './chunk-EKNEBGNW.js';
3
+ import { useSduiSandbox, useSduiAdapter, hostRawFetchOf, defaultAdapter, useSduiAssetResolver } from './chunk-DE7FPWVJ.js';
4
+ import { Spinner, Icon, IconButton } from './chunk-45GIMFRP.js';
4
5
  import { boxed } from './chunk-3JTBOABA.js';
5
6
  import { useState, useRef, useEffect, useCallback } from 'react';
6
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
8
 
8
9
  var THUMB_SIZE = 72;
9
- var FILE_TILE_WIDTH = 168;
10
- var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
11
- "jpg",
12
- "jpeg",
13
- "jpe",
14
- "jfif",
15
- "pjpeg",
16
- "pjp",
17
- "png",
18
- "apng",
19
- "gif",
20
- "webp",
21
- "avif",
22
- "bmp",
23
- "svg",
24
- "ico",
25
- "heic",
26
- "heif",
27
- "tif",
28
- "tiff"
29
- ]);
30
- var MIME_SHAPE = /^[a-z0-9][\w.+-]*\/[\w.+-]+/i;
31
- function urlOf(item) {
32
- return typeof item.url === "string" ? item.url : "";
33
- }
34
- function itemMime(item) {
35
- const record = item;
36
- for (const key of ["mimeType", "mime_type", "type"]) {
37
- const value = record[key];
38
- if (typeof value === "string" && MIME_SHAPE.test(value.trim())) return value.trim().toLowerCase();
39
- }
40
- return null;
41
- }
42
- function extensionOfPath(path) {
43
- const segment = path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
44
- const dot = segment.lastIndexOf(".");
45
- if (dot <= 0) return null;
46
- const ext = segment.slice(dot + 1).toLowerCase();
47
- return /^[a-z0-9]{1,10}$/.test(ext) ? ext : null;
48
- }
49
- function extensionOfUrl(url) {
50
- if (/^(blob|data):/i.test(url.trim())) return null;
51
- return extensionOfPath(url.split(/[?#]/, 1)[0]);
52
- }
53
- function dataUrlMime(url) {
54
- const match = /^data:([^;,]+)/i.exec(url.trim());
55
- return match && MIME_SHAPE.test(match[1]) ? match[1].toLowerCase() : null;
56
- }
57
- function itemLooksLikeImage(item, fileType) {
58
- const mime = itemMime(item) ?? (fileType && MIME_SHAPE.test(fileType) ? fileType.toLowerCase() : null) ?? dataUrlMime(urlOf(item));
59
- if (mime) return mime.startsWith("image/");
60
- const ext = extensionOfUrl(urlOf(item)) ?? (item.name ? extensionOfPath(item.name) : null);
61
- if (ext) return IMAGE_EXTENSIONS.has(ext);
62
- return true;
63
- }
64
- function fileBadgeOf(item, fileType) {
65
- const ext = (item.name ? extensionOfPath(item.name) : null) ?? extensionOfUrl(urlOf(item));
66
- if (ext) return ext.toUpperCase();
67
- const mime = itemMime(item) ?? (fileType && MIME_SHAPE.test(fileType) ? fileType.toLowerCase() : null);
68
- const subtype = mime?.split("/")[1]?.split(/[;+]/)[0];
69
- return subtype && /^[a-z0-9]{1,5}$/.test(subtype) ? subtype.toUpperCase() : null;
70
- }
71
- function displayNameOf(item) {
72
- if (item.name && item.name.trim() !== "") return item.name;
73
- const url = urlOf(item);
74
- if (!/^(blob|data):/i.test(url.trim())) {
75
- const path = url.split(/[?#]/, 1)[0];
76
- const segment = path.slice(path.lastIndexOf("/") + 1);
77
- if (segment !== "") {
78
- try {
79
- return decodeURIComponent(segment);
80
- } catch {
81
- return segment;
82
- }
83
- }
84
- }
85
- return "\uCCA8\uBD80 \uD30C\uC77C";
86
- }
87
- function safeFileHref(url) {
88
- const value = url.replace(/[\u0000-\u0020]/g, "");
89
- if (value === "") return null;
90
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value);
91
- if (!scheme) return url;
92
- return /^(https?|blob)$/i.test(scheme[1]) ? url : null;
93
- }
94
10
  function acceptIsImageOnly(accept) {
95
11
  const rules = accept.split(",").map((rule) => rule.trim().toLowerCase()).filter(Boolean);
96
12
  if (rules.length === 0) return false;
@@ -421,7 +337,7 @@ function FileDropzoneImpl({
421
337
  ) : null,
422
338
  items.length > 0 ? /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "var(--wp-space-2)" }, children: items.map((item) => {
423
339
  const fileType = fileTypesRef.current.get(item.id);
424
- const asImage = itemLooksLikeImage(item, fileType);
340
+ const asImage = looksLikeImage(item, fileType);
425
341
  const resolved = resolveAsset(item.url, {
426
342
  component: "FileDropzone",
427
343
  prop: asImage ? "src" : "href",
@@ -449,6 +365,7 @@ function FileDropzoneImpl({
449
365
  {
450
366
  src: resolved,
451
367
  alt: item.name,
368
+ mimeType: fileMimeOf(item) ?? fileType,
452
369
  thumbStyle: {
453
370
  width: THUMB_SIZE,
454
371
  height: THUMB_SIZE,
@@ -460,75 +377,15 @@ function FileDropzoneImpl({
460
377
  }
461
378
  );
462
379
  } else {
463
- const href = sandbox || resolved === null ? null : safeFileHref(resolved);
464
- const displayName = displayNameOf(item);
465
- const badge = fileBadgeOf(item, fileType);
466
- const tileStyle = {
467
- display: "flex",
468
- alignItems: "center",
469
- gap: "var(--wp-space-2)",
470
- boxSizing: "border-box",
471
- width: FILE_TILE_WIDTH,
472
- height: THUMB_SIZE,
473
- padding: "0 var(--wp-space-2)",
474
- borderRadius: "var(--wp-radius-md)",
475
- border: `var(--wp-border-w) solid ${hovered && href ? "var(--wp-border-strong)" : "var(--wp-border-subtle)"}`,
476
- background: "var(--wp-surface-sunken)",
477
- color: "var(--wp-text-default)",
478
- textDecoration: "none",
479
- transition: "var(--wp-transition-surface)"
480
- };
481
- const tileBody = /* @__PURE__ */ jsxs(Fragment, { children: [
482
- /* @__PURE__ */ jsxs(
483
- "span",
484
- {
485
- style: {
486
- display: "flex",
487
- flexDirection: "column",
488
- alignItems: "center",
489
- gap: 2,
490
- flex: "none",
491
- minWidth: 28,
492
- color: "var(--wp-text-muted)"
493
- },
494
- children: [
495
- /* @__PURE__ */ jsx(Icon, { name: "file-text", size: 24 }),
496
- badge ? /* @__PURE__ */ jsx("span", { className: "wp-overline", style: { lineHeight: 1 }, children: badge }) : null
497
- ]
498
- }
499
- ),
500
- /* @__PURE__ */ jsx(
501
- "span",
502
- {
503
- className: "wp-caption",
504
- style: {
505
- flex: 1,
506
- minWidth: 0,
507
- color: "var(--wp-text-default)",
508
- display: "-webkit-box",
509
- WebkitLineClamp: 2,
510
- WebkitBoxOrient: "vertical",
511
- overflow: "hidden",
512
- wordBreak: "break-all"
513
- },
514
- children: displayName
515
- }
516
- )
517
- ] });
518
- preview = href ? /* @__PURE__ */ jsx(
519
- "a",
380
+ preview = /* @__PURE__ */ jsx(
381
+ FileTile,
520
382
  {
521
- href,
522
- target: "_blank",
523
- rel: "noopener",
524
- title: displayName,
525
- "aria-label": `${displayName}${badge ? ` (${badge})` : ""} \u2014 \uC0C8 \uD0ED\uC5D0\uC11C \uC5F4\uAE30`,
526
- "data-file-tile": "",
527
- style: tileStyle,
528
- onClick: (event) => event.stopPropagation(),
529
- children: tileBody
383
+ href: sandbox || resolved === null ? null : safeFileHref(resolved),
384
+ name: fileDisplayNameOf(item),
385
+ badge: fileBadgeOf(item, fileType),
386
+ highlighted: hovered
530
387
  }
531
- ) : /* @__PURE__ */ jsx("span", { title: displayName, "data-file-tile": "", style: tileStyle, children: tileBody });
388
+ );
532
389
  }
533
390
  return /* @__PURE__ */ jsxs(
534
391
  "div",
@@ -1,5 +1,5 @@
1
- import { buildCascadingTree } from './chunk-MIFCOTLQ.js';
2
- import { Input, IconButton, EmptyState, Icon, Badge } from './chunk-NKRZVRGH.js';
1
+ import { buildCascadingTree } from './chunk-QDCP6P3K.js';
2
+ import { Input, IconButton, EmptyState, Icon, Badge } from './chunk-45GIMFRP.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import React, { useMemo, useState, useRef, useEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
1
  import { DEV } from './chunk-TAVKGYH5.js';
2
- import { warnOnce } from './chunk-NKRZVRGH.js';
2
+ import { warnOnce } from './chunk-45GIMFRP.js';
3
3
  import { createContext, useContext } from 'react';
4
4
 
5
5
  function isSafeNavigationTarget(to) {
@@ -1,4 +1,4 @@
1
- import { Icon } from './chunk-NKRZVRGH.js';
1
+ import { Icon } from './chunk-45GIMFRP.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useRef, useState, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,9 +1,200 @@
1
1
  import { useOverlayLayer, OverlayContainerContext, getOverlayRoot } from './chunk-R2ORCOTG.js';
2
- import { IconButton } from './chunk-NKRZVRGH.js';
3
- import { useState, useRef, useCallback, useEffect, useContext } from 'react';
2
+ import { useSduiSandbox } from './chunk-DE7FPWVJ.js';
3
+ import { IconButton, Icon } from './chunk-45GIMFRP.js';
4
+ import React, { useState, useRef, useCallback, useEffect, useContext } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
6
  import { createPortal } from 'react-dom';
5
- import { jsxs, jsx } from 'react/jsx-runtime';
6
7
 
8
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
9
+ "jpg",
10
+ "jpeg",
11
+ "jpe",
12
+ "jfif",
13
+ "pjpeg",
14
+ "pjp",
15
+ "png",
16
+ "apng",
17
+ "gif",
18
+ "webp",
19
+ "avif",
20
+ "bmp",
21
+ "svg",
22
+ "ico",
23
+ "heic",
24
+ "heif",
25
+ "tif",
26
+ "tiff"
27
+ ]);
28
+ var MIME_SHAPE = /^[a-z0-9][\w.+-]*\/[\w.+-]+/i;
29
+ function asMime(value) {
30
+ return typeof value === "string" && MIME_SHAPE.test(value.trim()) ? value.trim().toLowerCase() : null;
31
+ }
32
+ function urlOf(item) {
33
+ return typeof item.url === "string" ? item.url : "";
34
+ }
35
+ function nameOf(item) {
36
+ return typeof item.name === "string" && item.name.trim() !== "" ? item.name : null;
37
+ }
38
+ function fileMimeOf(item) {
39
+ return asMime(item.mimeType) ?? asMime(item.mime_type) ?? asMime(item.type);
40
+ }
41
+ function extensionOfPath(path) {
42
+ const segment = path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
43
+ const dot = segment.lastIndexOf(".");
44
+ if (dot <= 0) return null;
45
+ const ext = segment.slice(dot + 1).toLowerCase();
46
+ return /^[a-z0-9]{1,10}$/.test(ext) ? ext : null;
47
+ }
48
+ function extensionOfUrl(url) {
49
+ if (/^(blob|data):/i.test(url.trim())) return null;
50
+ return extensionOfPath(url.split(/[?#]/, 1)[0]);
51
+ }
52
+ function dataUrlMime(url) {
53
+ const match = /^data:([^;,]+)/i.exec(url.trim());
54
+ return match ? asMime(match[1]) : null;
55
+ }
56
+ function looksLikeImage(item, fileType) {
57
+ const mime = fileMimeOf(item) ?? asMime(fileType) ?? dataUrlMime(urlOf(item));
58
+ if (mime) return mime.startsWith("image/");
59
+ const name = nameOf(item);
60
+ const ext = extensionOfUrl(urlOf(item)) ?? (name ? extensionOfPath(name) : null);
61
+ if (ext) return IMAGE_EXTENSIONS.has(ext);
62
+ return true;
63
+ }
64
+ function fileBadgeOf(item, fileType) {
65
+ const name = nameOf(item);
66
+ const ext = (name ? extensionOfPath(name) : null) ?? extensionOfUrl(urlOf(item));
67
+ if (ext) return ext.toUpperCase();
68
+ const mime = fileMimeOf(item) ?? asMime(fileType);
69
+ const subtype = mime?.split("/")[1]?.split(/[;+]/)[0];
70
+ return subtype && /^[a-z0-9]{1,5}$/.test(subtype) ? subtype.toUpperCase() : null;
71
+ }
72
+ function fileDisplayNameOf(item) {
73
+ const name = nameOf(item);
74
+ if (name) return name;
75
+ const url = urlOf(item);
76
+ if (!/^(blob|data):/i.test(url.trim())) {
77
+ const path = url.split(/[?#]/, 1)[0];
78
+ const segment = path.slice(path.lastIndexOf("/") + 1);
79
+ if (segment !== "") {
80
+ try {
81
+ return decodeURIComponent(segment);
82
+ } catch {
83
+ return segment;
84
+ }
85
+ }
86
+ }
87
+ return "\uCCA8\uBD80 \uD30C\uC77C";
88
+ }
89
+ function safeFileHref(url) {
90
+ const value = url.replace(/[\u0000- ]/g, "");
91
+ if (value === "") return null;
92
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value);
93
+ if (!scheme) return url;
94
+ return /^(https?|blob)$/i.test(scheme[1]) ? url : null;
95
+ }
96
+ var FILE_TILE_WIDTH = 168;
97
+ var FILE_TILE_HEIGHT = 72;
98
+ function FileTile({
99
+ href,
100
+ name,
101
+ badge,
102
+ layout = "row",
103
+ highlighted,
104
+ style,
105
+ className
106
+ }) {
107
+ const [ownHover, setOwnHover] = React.useState(false);
108
+ const hovered = highlighted ?? ownHover;
109
+ const tile = layout === "tile";
110
+ const tileStyle = {
111
+ display: "flex",
112
+ flexDirection: tile ? "column" : void 0,
113
+ alignItems: "center",
114
+ justifyContent: tile ? "center" : void 0,
115
+ gap: tile ? 2 : "var(--wp-space-2)",
116
+ boxSizing: "border-box",
117
+ width: FILE_TILE_WIDTH,
118
+ height: FILE_TILE_HEIGHT,
119
+ padding: tile ? "var(--wp-space-1)" : "0 var(--wp-space-2)",
120
+ borderRadius: "var(--wp-radius-md)",
121
+ border: `var(--wp-border-w) solid ${hovered && href ? "var(--wp-border-strong)" : "var(--wp-border-subtle)"}`,
122
+ background: "var(--wp-surface-sunken)",
123
+ color: tile ? "var(--wp-text-muted)" : "var(--wp-text-default)",
124
+ textDecoration: "none",
125
+ transition: "var(--wp-transition-surface)",
126
+ ...style
127
+ };
128
+ const mark = /* @__PURE__ */ jsxs(Fragment, { children: [
129
+ /* @__PURE__ */ jsx(Icon, { name: "file-text", size: tile ? 28 : 24 }),
130
+ badge ? /* @__PURE__ */ jsx("span", { className: "wp-overline", style: { lineHeight: 1 }, children: badge }) : null
131
+ ] });
132
+ const body = tile ? mark : /* @__PURE__ */ jsxs(Fragment, { children: [
133
+ /* @__PURE__ */ jsx(
134
+ "span",
135
+ {
136
+ style: {
137
+ display: "flex",
138
+ flexDirection: "column",
139
+ alignItems: "center",
140
+ gap: 2,
141
+ flex: "none",
142
+ minWidth: 28,
143
+ color: "var(--wp-text-muted)"
144
+ },
145
+ children: mark
146
+ }
147
+ ),
148
+ /* @__PURE__ */ jsx(
149
+ "span",
150
+ {
151
+ className: "wp-caption",
152
+ style: {
153
+ flex: 1,
154
+ minWidth: 0,
155
+ color: "var(--wp-text-default)",
156
+ display: "-webkit-box",
157
+ WebkitLineClamp: 2,
158
+ WebkitBoxOrient: "vertical",
159
+ overflow: "hidden",
160
+ wordBreak: "break-all"
161
+ },
162
+ children: name
163
+ }
164
+ )
165
+ ] });
166
+ const hoverProps = highlighted === void 0 ? { onMouseEnter: () => setOwnHover(true), onMouseLeave: () => setOwnHover(false) } : {};
167
+ if (href) {
168
+ return /* @__PURE__ */ jsx(
169
+ "a",
170
+ {
171
+ href,
172
+ target: "_blank",
173
+ rel: "noopener",
174
+ title: name,
175
+ "aria-label": `${name}${badge ? ` (${badge})` : ""} \u2014 \uC0C8 \uD0ED\uC5D0\uC11C \uC5F4\uAE30`,
176
+ "data-file-tile": "",
177
+ className,
178
+ style: tileStyle,
179
+ onClick: (event) => event.stopPropagation(),
180
+ ...hoverProps,
181
+ children: body
182
+ }
183
+ );
184
+ }
185
+ return /* @__PURE__ */ jsx(
186
+ "span",
187
+ {
188
+ title: name,
189
+ "data-file-tile": "",
190
+ className,
191
+ style: tileStyle,
192
+ role: tile ? "img" : void 0,
193
+ "aria-label": tile ? `${name}${badge ? ` (${badge})` : ""}` : void 0,
194
+ children: body
195
+ }
196
+ );
197
+ }
7
198
  var MIN_SCALE = 1;
8
199
  var MAX_SCALE = 8;
9
200
  var BUTTON_ZOOM_STEP = 1.5;
@@ -15,6 +206,10 @@ function clamp(value, min, max) {
15
206
  function ImageViewer({ src, alt, open, onClose }) {
16
207
  const [view, setView] = useState(INITIAL_VIEW);
17
208
  const [dragging, setDragging] = useState(false);
209
+ const [failedSrc, setFailedSrc] = useState(null);
210
+ const sandbox = useSduiSandbox();
211
+ const source = typeof src === "string" ? src : "";
212
+ const showImage = looksLikeImage({ url: source }) && failedSrc !== source;
18
213
  const rootRef = useRef(null);
19
214
  const stageRef = useRef(null);
20
215
  const imgRef = useRef(null);
@@ -40,7 +235,7 @@ function ImageViewer({ src, alt, open, onClose }) {
40
235
  const zoomAt = useCallback(
41
236
  (clientX, clientY, factor) => {
42
237
  const stage = stageRef.current;
43
- if (!stage) return;
238
+ if (!stage || !imgRef.current) return;
44
239
  const prev = viewRef.current;
45
240
  const nextScale = clamp(prev.scale * factor, MIN_SCALE, MAX_SCALE);
46
241
  if (nextScale === prev.scale) return;
@@ -180,13 +375,14 @@ function ImageViewer({ src, alt, open, onClose }) {
180
375
  onPointerMove: onStagePointerMove,
181
376
  onPointerUp: endDrag,
182
377
  onPointerCancel: endDrag,
183
- children: /* @__PURE__ */ jsx(
378
+ children: showImage ? /* @__PURE__ */ jsx(
184
379
  "img",
185
380
  {
186
381
  ref: imgRef,
187
382
  src,
188
383
  alt: alt ?? "",
189
384
  draggable: false,
385
+ onError: () => setFailedSrc(source),
190
386
  style: {
191
387
  maxWidth: "calc(100vw - 96px)",
192
388
  maxHeight: "calc(100vh - 96px)",
@@ -196,6 +392,12 @@ function ImageViewer({ src, alt, open, onClose }) {
196
392
  borderRadius: view.scale === 1 ? "var(--wp-radius-sm)" : 0
197
393
  }
198
394
  }
395
+ ) : /* @__PURE__ */ jsx(
396
+ ViewerFileNotice,
397
+ {
398
+ name: alt && alt.trim() !== "" ? alt : fileDisplayNameOf({ url: source }),
399
+ href: sandbox ? null : safeFileHref(source)
400
+ }
199
401
  )
200
402
  }
201
403
  ),
@@ -238,7 +440,7 @@ function ImageViewer({ src, alt, open, onClose }) {
238
440
  icon: "zoom-out",
239
441
  label: "\uCD95\uC18C",
240
442
  size: "sm",
241
- disabled: view.scale <= MIN_SCALE,
443
+ disabled: !showImage || view.scale <= MIN_SCALE,
242
444
  onClick: () => zoomAtCenter(1 / BUTTON_ZOOM_STEP)
243
445
  }
244
446
  ),
@@ -248,7 +450,7 @@ function ImageViewer({ src, alt, open, onClose }) {
248
450
  icon: "zoom-in",
249
451
  label: "\uD655\uB300",
250
452
  size: "sm",
251
- disabled: view.scale >= MAX_SCALE,
453
+ disabled: !showImage || view.scale >= MAX_SCALE,
252
454
  onClick: () => zoomAtCenter(BUTTON_ZOOM_STEP)
253
455
  }
254
456
  ),
@@ -272,5 +474,40 @@ function ImageViewer({ src, alt, open, onClose }) {
272
474
  portalRoot
273
475
  );
274
476
  }
477
+ function ViewerFileNotice({ name, href }) {
478
+ return /* @__PURE__ */ jsxs(
479
+ "div",
480
+ {
481
+ role: "status",
482
+ style: {
483
+ display: "flex",
484
+ flexDirection: "column",
485
+ alignItems: "center",
486
+ gap: "var(--wp-space-2)",
487
+ maxWidth: "min(360px, calc(100vw - 48px))",
488
+ padding: "var(--wp-space-6)",
489
+ background: "var(--wp-surface-card)",
490
+ borderRadius: "var(--wp-radius-lg)",
491
+ boxShadow: "var(--wp-shadow-xl)",
492
+ textAlign: "center"
493
+ },
494
+ children: [
495
+ /* @__PURE__ */ jsx(Icon, { name: "file-text", size: 32, style: { color: "var(--wp-text-muted)" } }),
496
+ /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: { color: "var(--wp-text-default)", wordBreak: "break-all" }, children: name }),
497
+ /* @__PURE__ */ jsx("span", { className: "wp-caption", children: "\uC774\uBBF8\uC9C0\uB85C \uD45C\uC2DC\uD560 \uC218 \uC5C6\uB294 \uD30C\uC77C\uC785\uB2C8\uB2E4" }),
498
+ href ? /* @__PURE__ */ jsx(
499
+ "a",
500
+ {
501
+ href,
502
+ target: "_blank",
503
+ rel: "noopener",
504
+ className: "wp-btn wp-btn--secondary wp-btn--sm",
505
+ children: "\uC0C8 \uD0ED\uC5D0\uC11C \uC5F4\uAE30"
506
+ }
507
+ ) : null
508
+ ]
509
+ }
510
+ );
511
+ }
275
512
 
276
- export { ImageViewer };
513
+ export { FILE_TILE_WIDTH, FileTile, IMAGE_EXTENSIONS, ImageViewer, fileBadgeOf, fileDisplayNameOf, fileMimeOf, looksLikeImage, safeFileHref };