@valu/block-components 0.2.0 → 0.3.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 CHANGED
@@ -1,8 +1,8 @@
1
1
  import { forwardRef, useId, useState, useCallback, useRef, useMemo, useEffect } from 'react';
2
2
  import { QueryClientProvider, QueryClient, useInfiniteQuery, keepPreviousData } from '@tanstack/react-query';
3
- import { VisuallyHidden, Icon, Popover, ToggleControl, Spinner, Button, CheckboxControl } from '@wordpress/components';
3
+ import { VisuallyHidden, Icon, Button, Popover, ToggleControl, Spinner, CheckboxControl } from '@wordpress/components';
4
4
  import { __, sprintf, _n } from '@wordpress/i18n';
5
- import { search, closeSmall, dragHandle, caution, chevronUp, chevronDown, people } from '@wordpress/icons';
5
+ import { search, closeSmall, upload, dragHandle, caution, chevronUp, chevronDown, people } from '@wordpress/icons';
6
6
  import styled from '@emotion/styled';
7
7
  import { css, CacheProvider } from '@emotion/react';
8
8
  import { v4 } from 'uuid';
@@ -17,6 +17,7 @@ import { useSensors, useSensor, MouseSensor, TouchSensor, KeyboardSensor, DndCon
17
17
  import { sortableKeyboardCoordinates, arrayMove, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
18
18
  import { CSS } from '@dnd-kit/utilities';
19
19
  import { store } from '@wordpress/core-data';
20
+ import { MediaUpload as MediaUpload$1 } from '@wordpress/media-utils';
20
21
 
21
22
  // components/content-picker/ContentPicker.tsx
22
23
 
@@ -2233,6 +2234,22 @@ var SummaryCell = styled.td`
2233
2234
  color: ${tokens2.gray600};
2234
2235
  font-variant-numeric: tabular-nums;
2235
2236
  `;
2237
+ var FillerCell = styled.td`
2238
+ width: 100%;
2239
+ padding: 0;
2240
+ `;
2241
+ var FillerHeaderCell = styled.th`
2242
+ width: 100%;
2243
+ padding: 0;
2244
+
2245
+ ${(p) => p.$sticky && css`
2246
+ position: sticky;
2247
+ top: 0;
2248
+ z-index: ${tokens2.zHeader};
2249
+ background: ${tokens2.bg};
2250
+ box-shadow: ${tokens2.shadowStickyTop};
2251
+ `};
2252
+ `;
2236
2253
  var CellControlWrap = styled.div`
2237
2254
  display: inline-flex;
2238
2255
  align-items: center;
@@ -2301,7 +2318,8 @@ var MatrixHeader = ({
2301
2318
  children: renderHeader ? renderHeader(col) : col.label
2302
2319
  },
2303
2320
  col.id
2304
- ))
2321
+ )),
2322
+ /* @__PURE__ */ jsx(FillerHeaderCell, { "aria-hidden": true, $sticky: stickyHeader })
2305
2323
  ] }) });
2306
2324
  }
2307
2325
  const TOP_ROW_HEIGHT = "36px";
@@ -2358,7 +2376,8 @@ var MatrixHeader = ({
2358
2376
  },
2359
2377
  group.id
2360
2378
  );
2361
- })
2379
+ }),
2380
+ /* @__PURE__ */ jsx(FillerHeaderCell, { "aria-hidden": true, rowSpan: 2, $sticky: stickyHeader })
2362
2381
  ] }),
2363
2382
  /* @__PURE__ */ jsx("tr", { children: groups.map((group) => {
2364
2383
  const open = isExpanded(group, expandedGroups);
@@ -2570,7 +2589,8 @@ var MatrixRowView = ({
2570
2589
  },
2571
2590
  `${group.id}::${col.id}`
2572
2591
  ));
2573
- })
2592
+ }),
2593
+ /* @__PURE__ */ jsx(FillerCell, { "aria-hidden": true })
2574
2594
  ] });
2575
2595
  };
2576
2596
  var EmotionCacheProvider2 = ({ children }) => {
@@ -2653,6 +2673,7 @@ var Matrix = (props) => {
2653
2673
  renderHeader,
2654
2674
  stickyHeader = true,
2655
2675
  stickyFirstColumn = true,
2676
+ rowHeaderWidth = 220,
2656
2677
  maxHeight = DEFAULT_MAX_HEIGHT,
2657
2678
  caption,
2658
2679
  labels,
@@ -2672,9 +2693,16 @@ var Matrix = (props) => {
2672
2693
  onChange: onExpandedChange
2673
2694
  });
2674
2695
  const maxHeightCss = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
2675
- const masterLeftOffset = "220px";
2696
+ const cssLength = (v) => typeof v === "number" ? `${v}px` : v;
2697
+ const rowHeaderWidthCss = cssLength(rowHeaderWidth);
2698
+ const masterWidthCss = master ? cssLength(master.width ?? 96) : "0";
2699
+ const masterLeftOffset = rowHeaderWidthCss;
2676
2700
  return /* @__PURE__ */ jsx(EmotionCacheProvider2, { children: /* @__PURE__ */ jsx(Root2, { id: rootId, className, children: /* @__PURE__ */ jsx(Viewport, { $maxHeight: maxHeightCss, children: /* @__PURE__ */ jsxs(Table, { children: [
2677
2701
  caption && /* @__PURE__ */ jsx(Caption, { children: caption }),
2702
+ /* @__PURE__ */ jsxs("colgroup", { children: [
2703
+ /* @__PURE__ */ jsx("col", { style: { width: rowHeaderWidthCss } }),
2704
+ master && /* @__PURE__ */ jsx("col", { style: { width: masterWidthCss } })
2705
+ ] }),
2678
2706
  /* @__PURE__ */ jsx(
2679
2707
  MatrixHeader,
2680
2708
  {
@@ -2712,7 +2740,1543 @@ var Matrix = (props) => {
2712
2740
  )) })
2713
2741
  ] }) }) }) });
2714
2742
  };
2743
+ var SelectedRow2 = styled.li`
2744
+ container-type: inline-size;
2745
+ container-name: valu-dp-row;
2746
+ display: flex;
2747
+ align-items: center;
2748
+ gap: ${tokens.spaceSm};
2749
+ padding: 2px ${tokens.spaceSm};
2750
+ background: ${({ missing }) => missing ? tokens.dangerSoft : tokens.bg};
2751
+ border: 1px solid ${({ missing }) => missing ? tokens.dangerLine : tokens.gray300};
2752
+ border-radius: ${tokens.radiusMd};
2753
+ min-height: ${tokens.rowMinHeight};
2754
+ box-shadow: ${({ dragging }) => dragging ? tokens.shadowPopover : "none"};
2755
+ transition: box-shadow ${tokens.transitionFast}, border-color ${tokens.transitionFast};
2756
+
2757
+ &:hover {
2758
+ border-color: ${({ missing }) => missing ? tokens.dangerLine : tokens.gray600};
2759
+ }
2760
+ `;
2761
+ var WIDE_ROW = "380px";
2762
+ var Option2 = styled.li`
2763
+ container-type: inline-size;
2764
+ container-name: valu-dp-row;
2765
+ display: flex;
2766
+ align-items: center;
2767
+ gap: ${tokens.spaceSm};
2768
+ padding: 4px ${tokens.spaceMd};
2769
+ cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
2770
+ opacity: ${({ disabled }) => disabled ? 0.5 : 1};
2771
+ min-height: ${tokens.suggestionMinHeight};
2772
+ background: ${({ highlighted }) => highlighted ? tokens.accentActive : "transparent"};
2773
+ position: relative;
2774
+ transition: background ${tokens.transitionFast};
2775
+
2776
+ &:hover {
2777
+ background: ${({ highlighted, disabled }) => disabled ? "transparent" : highlighted ? tokens.accentActive : tokens.accentHover};
2778
+ }
2779
+
2780
+ &:hover [data-valu-title] {
2781
+ color: ${tokens.accent};
2782
+ }
2783
+
2784
+ ${({ highlighted }) => highlighted && css`
2785
+ [data-valu-title] {
2786
+ color: ${tokens.accent};
2787
+ }
2788
+ &::before {
2789
+ content: '';
2790
+ position: absolute;
2791
+ left: 0;
2792
+ top: 4px;
2793
+ bottom: 4px;
2794
+ width: 2px;
2795
+ border-radius: 0 ${tokens.radiusSm} ${tokens.radiusSm} 0;
2796
+ background: ${tokens.accent};
2797
+ }
2798
+ `}
2799
+ `;
2800
+ var DocRowBody = styled.div`
2801
+ flex: 1;
2802
+ min-width: 0;
2803
+ display: flex;
2804
+ flex-direction: column;
2805
+ gap: 0;
2806
+
2807
+ @container valu-dp-row (min-width: ${WIDE_ROW}) {
2808
+ flex-direction: row;
2809
+ align-items: baseline;
2810
+ gap: ${tokens.spaceMd};
2811
+ }
2812
+ `;
2813
+ var FilenameText = styled.span`
2814
+ font-size: ${tokens.fontSizeSmall};
2815
+ color: ${tokens.gray600};
2816
+ white-space: nowrap;
2817
+ overflow: hidden;
2818
+ text-overflow: ellipsis;
2819
+ display: block;
2820
+ line-height: 1.3;
2821
+ min-width: 0;
2822
+
2823
+ @container valu-dp-row (min-width: ${WIDE_ROW}) {
2824
+ flex: 1;
2825
+ }
2826
+ `;
2827
+ var DocMetaInline = styled.div`
2828
+ display: none;
2829
+
2830
+ @container valu-dp-row (min-width: ${WIDE_ROW}) {
2831
+ display: inline-flex;
2832
+ align-items: center;
2833
+ gap: ${tokens.spaceSm};
2834
+ flex: 0 0 auto;
2835
+ color: ${tokens.gray600};
2836
+ font-size: ${tokens.fontSizeSmall};
2837
+ white-space: nowrap;
2838
+ }
2839
+ `;
2840
+ var Root3 = styled.div`
2841
+ color: ${tokens.fg};
2842
+ font-size: ${tokens.fontSizeBody};
2843
+
2844
+ * {
2845
+ box-sizing: border-box;
2846
+ }
2847
+ `;
2848
+ var ActionsRow = styled.div`
2849
+ display: flex;
2850
+ align-items: center;
2851
+ gap: ${tokens.spaceSm};
2852
+ margin-top: ${tokens.spaceSm};
2853
+ `;
2854
+ var FileMeta = styled.span`
2855
+ font-size: ${tokens.fontSizeSmall};
2856
+ color: ${tokens.gray600};
2857
+ white-space: nowrap;
2858
+ display: inline-flex;
2859
+ align-items: center;
2860
+ gap: ${tokens.spaceXs};
2861
+
2862
+ &::before {
2863
+ content: '·';
2864
+ opacity: 0.6;
2865
+ }
2866
+
2867
+ &:first-of-type::before {
2868
+ content: '';
2869
+ }
2870
+ `;
2871
+ styled.div`
2872
+ display: flex;
2873
+ align-items: center;
2874
+ gap: ${tokens.spaceSm};
2875
+ flex-wrap: wrap;
2876
+ min-width: 0;
2877
+ color: ${tokens.gray600};
2878
+ `;
2879
+ var ExtensionPill = styled.span`
2880
+ flex: 0 0 auto;
2881
+ display: inline-flex;
2882
+ align-items: center;
2883
+ justify-content: center;
2884
+ min-width: 38px;
2885
+ padding: 2px 6px;
2886
+ font-size: 10px;
2887
+ font-weight: ${tokens.fontWeightBold};
2888
+ letter-spacing: 0.04em;
2889
+ text-transform: uppercase;
2890
+ border-radius: ${tokens.radiusSm};
2891
+ border: 1px solid currentColor;
2892
+ line-height: 1.4;
2893
+ white-space: nowrap;
2894
+
2895
+ ${({ tone = "gray" }) => {
2896
+ switch (tone) {
2897
+ case "red":
2898
+ return css`
2899
+ color: #c0392b;
2900
+ background: #fdecea;
2901
+ `;
2902
+ case "blue":
2903
+ return css`
2904
+ color: #1a4fb8;
2905
+ background: #e8efff;
2906
+ `;
2907
+ case "green":
2908
+ return css`
2909
+ color: #1f6b3a;
2910
+ background: #e6f4ea;
2911
+ `;
2912
+ case "orange":
2913
+ return css`
2914
+ color: #b15a06;
2915
+ background: #fff1de;
2916
+ `;
2917
+ default:
2918
+ return css`
2919
+ color: ${tokens.gray900};
2920
+ background: ${tokens.gray100};
2921
+ `;
2922
+ }
2923
+ }}
2924
+ `;
2925
+ var generateKey2 = () => v4();
2926
+ var aria2 = (rootId, suffix) => `${rootId}-${suffix}`;
2927
+ var isMaxedOut2 = (value, maxItems) => Number.isFinite(maxItems) && value.length >= maxItems;
2928
+ var DEFAULT_DOCUMENT_MIME_TYPES = [
2929
+ "application/pdf",
2930
+ "application/msword",
2931
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
2932
+ "application/vnd.ms-excel",
2933
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
2934
+ "application/vnd.ms-powerpoint",
2935
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
2936
+ ];
2937
+ var decodeRendered = (raw) => {
2938
+ if (typeof raw === "string") return decodeEntities(raw);
2939
+ if (raw && typeof raw === "object" && "rendered" in raw) {
2940
+ const rendered = raw.rendered;
2941
+ if (typeof rendered === "string") return decodeEntities(rendered);
2942
+ }
2943
+ return "";
2944
+ };
2945
+ var basenameFromUrl = (url) => {
2946
+ if (!url) return "";
2947
+ const cleaned = url.split("#")[0].split("?")[0];
2948
+ const segments = cleaned.split("/");
2949
+ return segments[segments.length - 1] ?? "";
2950
+ };
2951
+ var extensionFromUrl = (url) => {
2952
+ const base = basenameFromUrl(url);
2953
+ const dot = base.lastIndexOf(".");
2954
+ if (dot <= 0 || dot === base.length - 1) return "";
2955
+ return base.slice(dot + 1).toUpperCase();
2956
+ };
2957
+ var formatFileSize = (bytes) => {
2958
+ if (!bytes || bytes <= 0 || !Number.isFinite(bytes)) return "";
2959
+ const units = ["B", "KB", "MB", "GB", "TB"];
2960
+ let value = bytes;
2961
+ let unit = 0;
2962
+ while (value >= 1024 && unit < units.length - 1) {
2963
+ value /= 1024;
2964
+ unit += 1;
2965
+ }
2966
+ const fixed = value >= 100 || unit === 0 ? value.toFixed(0) : value.toFixed(1);
2967
+ return `${fixed} ${units[unit]}`;
2968
+ };
2969
+ var formatModified = (iso) => {
2970
+ if (!iso) return "";
2971
+ const date = new Date(iso);
2972
+ if (Number.isNaN(date.getTime())) return "";
2973
+ try {
2974
+ return new Intl.DateTimeFormat(void 0, { dateStyle: "medium" }).format(date);
2975
+ } catch {
2976
+ return date.toISOString().slice(0, 10);
2977
+ }
2978
+ };
2979
+ var mapMediaToDocumentItem = (raw) => {
2980
+ const url = raw.source_url ?? raw.url ?? raw.link ?? "";
2981
+ const filename = raw.filename || basenameFromUrl(url);
2982
+ const title = decodeRendered(raw.title) || filename;
2983
+ const mimeType = raw.mime_type ?? raw.mime ?? "";
2984
+ const extension = extensionFromUrl(url) || extensionFromUrl(filename);
2985
+ const fileSize = raw.media_details?.filesize;
2986
+ const modified = raw.modified ?? raw.modified_gmt ?? raw.date;
2987
+ return {
2988
+ key: generateKey2(),
2989
+ id: raw.id,
2990
+ title,
2991
+ filename,
2992
+ url,
2993
+ mimeType,
2994
+ extension,
2995
+ fileSize,
2996
+ modified,
2997
+ raw
2998
+ };
2999
+ };
3000
+ var dedupeAgainst2 = (items, value, enabled) => {
3001
+ if (!enabled || value.length === 0) return items;
3002
+ const ids = new Set(value.map((v) => v.id));
3003
+ return items.filter((i) => !ids.has(i.id));
3004
+ };
3005
+ var matchesAllowed = (item, allowedMimeTypes, allowedExtensions) => {
3006
+ const hasMime = allowedMimeTypes && allowedMimeTypes.length > 0;
3007
+ const hasExt = allowedExtensions && allowedExtensions.length > 0;
3008
+ if (!hasMime && !hasExt) return true;
3009
+ if (hasMime && allowedMimeTypes.includes(item.mimeType)) return true;
3010
+ if (hasExt) {
3011
+ const upper = item.extension.toUpperCase();
3012
+ if (allowedExtensions.some((e) => e.replace(/^\./, "").toUpperCase() === upper)) {
3013
+ return true;
3014
+ }
3015
+ }
3016
+ return false;
3017
+ };
3018
+ var extensionA11yLabel = (extension) => extension ? __("File type", "valu-block-components") + ": " + extension : __("Unknown file type", "valu-block-components");
3019
+ var SearchCombobox2 = forwardRef(
3020
+ ({
3021
+ rootId,
3022
+ value,
3023
+ onChange,
3024
+ onFocus,
3025
+ onBlur,
3026
+ onKeyDown,
3027
+ placeholder,
3028
+ label,
3029
+ hideLabelFromVision,
3030
+ help,
3031
+ disabled,
3032
+ open,
3033
+ listboxId,
3034
+ activeOptionId,
3035
+ labels
3036
+ }, ref) => {
3037
+ const inputId = aria2(rootId, "input");
3038
+ const helpId = help ? aria2(rootId, "help") : void 0;
3039
+ return /* @__PURE__ */ jsxs("div", { children: [
3040
+ hideLabelFromVision ? /* @__PURE__ */ jsx(VisuallyHidden, { as: "label", htmlFor: inputId, children: label }) : /* @__PURE__ */ jsx("label", { htmlFor: inputId, style: { display: "block", marginBottom: 4 }, children: label }),
3041
+ /* @__PURE__ */ jsxs(InputShell, { "aria-disabled": disabled || void 0, children: [
3042
+ /* @__PURE__ */ jsx(SearchIconWrap, { "aria-hidden": "true", children: /* @__PURE__ */ jsx(Icon, { icon: search, size: 20 }) }),
3043
+ /* @__PURE__ */ jsx(
3044
+ Input,
3045
+ {
3046
+ ref,
3047
+ id: inputId,
3048
+ type: "text",
3049
+ role: "combobox",
3050
+ "aria-expanded": open,
3051
+ "aria-controls": listboxId,
3052
+ "aria-autocomplete": "list",
3053
+ "aria-activedescendant": activeOptionId,
3054
+ "aria-describedby": helpId,
3055
+ "aria-label": hideLabelFromVision ? label : void 0,
3056
+ value,
3057
+ placeholder: placeholder ?? labels.input ?? __("Search documents", "valu-block-components"),
3058
+ disabled,
3059
+ autoComplete: "off",
3060
+ spellCheck: false,
3061
+ onChange: (event) => onChange(event.target.value),
3062
+ onFocus,
3063
+ onBlur,
3064
+ onKeyDown
3065
+ }
3066
+ ),
3067
+ value.length > 0 && !disabled ? /* @__PURE__ */ jsx(
3068
+ "button",
3069
+ {
3070
+ type: "button",
3071
+ onClick: () => onChange(""),
3072
+ "aria-label": __("Clear search", "valu-block-components"),
3073
+ style: {
3074
+ display: "flex",
3075
+ alignItems: "center",
3076
+ justifyContent: "center",
3077
+ background: "transparent",
3078
+ border: 0,
3079
+ padding: 2,
3080
+ cursor: "pointer",
3081
+ borderRadius: 2,
3082
+ color: "currentColor",
3083
+ opacity: 0.6
3084
+ },
3085
+ children: /* @__PURE__ */ jsx(Icon, { icon: closeSmall, size: 18 })
3086
+ }
3087
+ ) : null
3088
+ ] }),
3089
+ help ? /* @__PURE__ */ jsx("p", { id: helpId, style: { margin: "4px 0 0", fontSize: 11, opacity: 0.7 }, children: help }) : null
3090
+ ] });
3091
+ }
3092
+ );
3093
+ SearchCombobox2.displayName = "SearchCombobox";
3094
+ var toneFor2 = (extension) => {
3095
+ const ext = extension.toUpperCase();
3096
+ if (ext === "PDF") return "red";
3097
+ if (ext === "DOC" || ext === "DOCX" || ext === "RTF" || ext === "ODT") return "blue";
3098
+ if (ext === "XLS" || ext === "XLSX" || ext === "CSV" || ext === "ODS") return "green";
3099
+ if (ext === "PPT" || ext === "PPTX" || ext === "ODP") return "orange";
3100
+ return "gray";
3101
+ };
3102
+ var ExtensionBadge = ({ extension, label }) => {
3103
+ const visible = label ?? (extension || "?");
3104
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3105
+ /* @__PURE__ */ jsx(ExtensionPill, { tone: toneFor2(extension), "aria-hidden": "true", children: visible }),
3106
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: extensionA11yLabel(extension) })
3107
+ ] });
3108
+ };
3109
+ var SuggestionItem2 = ({
3110
+ item,
3111
+ optionId,
3112
+ highlighted,
3113
+ selected,
3114
+ showExtensionBadge,
3115
+ showFileSize,
3116
+ showModifiedDate,
3117
+ onHover,
3118
+ onSelect,
3119
+ render
3120
+ }) => {
3121
+ const ref = useRef(null);
3122
+ useEffect(() => {
3123
+ if (!highlighted || !ref.current) return;
3124
+ ref.current.scrollIntoView({ block: "nearest" });
3125
+ }, [highlighted]);
3126
+ const handleClick = useCallback(
3127
+ (event) => {
3128
+ event.preventDefault();
3129
+ onSelect();
3130
+ },
3131
+ [onSelect]
3132
+ );
3133
+ const handleMouseDown = useCallback((event) => {
3134
+ event.preventDefault();
3135
+ }, []);
3136
+ if (render) {
3137
+ return /* @__PURE__ */ jsx(
3138
+ Option2,
3139
+ {
3140
+ ref,
3141
+ role: "option",
3142
+ id: optionId,
3143
+ "aria-selected": selected,
3144
+ highlighted,
3145
+ onMouseEnter: onHover,
3146
+ onMouseDown: handleMouseDown,
3147
+ onClick: handleClick,
3148
+ children: render(item, { highlighted, selected })
3149
+ }
3150
+ );
3151
+ }
3152
+ const modified = showModifiedDate ? formatModified(item.modified) : "";
3153
+ const hasMeta = Boolean(showFileSize && item.fileSize || modified);
3154
+ return /* @__PURE__ */ jsxs(
3155
+ Option2,
3156
+ {
3157
+ ref,
3158
+ role: "option",
3159
+ id: optionId,
3160
+ "aria-selected": selected,
3161
+ highlighted,
3162
+ onMouseEnter: onHover,
3163
+ onMouseDown: handleMouseDown,
3164
+ onClick: handleClick,
3165
+ children: [
3166
+ showExtensionBadge ? /* @__PURE__ */ jsx(ExtensionBadge, { extension: item.extension }) : null,
3167
+ /* @__PURE__ */ jsxs(DocRowBody, { children: [
3168
+ /* @__PURE__ */ jsx(PrimaryText, { "data-valu-title": true, title: item.title, children: item.title }),
3169
+ /* @__PURE__ */ jsx(FilenameText, { title: item.filename, children: item.filename })
3170
+ ] }),
3171
+ hasMeta ? /* @__PURE__ */ jsxs(DocMetaInline, { children: [
3172
+ showFileSize && item.fileSize ? /* @__PURE__ */ jsx("span", { children: formatFileSize(item.fileSize) }) : null,
3173
+ modified ? /* @__PURE__ */ jsx("span", { children: modified }) : null
3174
+ ] }) : null
3175
+ ]
3176
+ }
3177
+ );
3178
+ };
3179
+ var SuggestionList2 = ({
3180
+ listboxId,
3181
+ rootId,
3182
+ suggestions,
3183
+ loading,
3184
+ fetchingMore,
3185
+ hasMore,
3186
+ error,
3187
+ query,
3188
+ highlightedIndex,
3189
+ selectedKeys,
3190
+ labels,
3191
+ showExtensionBadge,
3192
+ showFileSize,
3193
+ showModifiedDate,
3194
+ headerSlot,
3195
+ renderSuggestion,
3196
+ onHover,
3197
+ onSelect,
3198
+ onLoadMore
3199
+ }) => {
3200
+ const scrollRef = useRef(null);
3201
+ const sentinelRef = useRef(null);
3202
+ useEffect(() => {
3203
+ if (!hasMore || !sentinelRef.current || !scrollRef.current) return;
3204
+ const io = new IntersectionObserver(
3205
+ (entries) => {
3206
+ for (const e of entries) {
3207
+ if (e.isIntersecting) onLoadMore();
3208
+ }
3209
+ },
3210
+ { root: scrollRef.current, rootMargin: "120px" }
3211
+ );
3212
+ io.observe(sentinelRef.current);
3213
+ return () => io.disconnect();
3214
+ }, [hasMore, onLoadMore]);
3215
+ const header = headerSlot ? /* @__PURE__ */ jsx(StickyHeader, { children: headerSlot }) : null;
3216
+ if (error) {
3217
+ return /* @__PURE__ */ jsxs(ListboxScroll, { ref: scrollRef, children: [
3218
+ header,
3219
+ /* @__PURE__ */ jsx(ErrorState, { role: "alert", children: labels.empty ?? __("Search failed. Please try again.", "valu-block-components") })
3220
+ ] });
3221
+ }
3222
+ if (loading && suggestions.length === 0) {
3223
+ return /* @__PURE__ */ jsxs(ListboxScroll, { ref: scrollRef, children: [
3224
+ header,
3225
+ /* @__PURE__ */ jsxs(LoadingState, { children: [
3226
+ /* @__PURE__ */ jsx(Spinner, {}),
3227
+ labels.loading ?? __("Searching\u2026", "valu-block-components")
3228
+ ] })
3229
+ ] });
3230
+ }
3231
+ if (suggestions.length === 0) {
3232
+ const emptyMessage = query.trim().length > 0 ? labels.noDocumentsFound ?? __("No documents match this search.", "valu-block-components") : __("Type to search documents.", "valu-block-components");
3233
+ return /* @__PURE__ */ jsxs(ListboxScroll, { ref: scrollRef, children: [
3234
+ header,
3235
+ /* @__PURE__ */ jsx(EmptyState, { children: emptyMessage })
3236
+ ] });
3237
+ }
3238
+ return /* @__PURE__ */ jsxs(ListboxScroll, { ref: scrollRef, children: [
3239
+ header,
3240
+ /* @__PURE__ */ jsxs(
3241
+ Listbox,
3242
+ {
3243
+ role: "listbox",
3244
+ id: listboxId,
3245
+ "aria-label": labels.input ?? __("Document suggestions", "valu-block-components"),
3246
+ children: [
3247
+ suggestions.map((item, index) => {
3248
+ const optionId = aria2(rootId, `option-${index}`);
3249
+ return /* @__PURE__ */ jsx(
3250
+ SuggestionItem2,
3251
+ {
3252
+ item,
3253
+ optionId,
3254
+ highlighted: index === highlightedIndex,
3255
+ selected: selectedKeys.has(item.key),
3256
+ showExtensionBadge,
3257
+ showFileSize,
3258
+ showModifiedDate,
3259
+ onHover: () => onHover(index),
3260
+ onSelect: () => onSelect(item),
3261
+ ...renderSuggestion ? { render: renderSuggestion } : {}
3262
+ },
3263
+ item.key
3264
+ );
3265
+ }),
3266
+ hasMore ? /* @__PURE__ */ jsx(LoadMoreRow, { children: /* @__PURE__ */ jsx(
3267
+ Button,
3268
+ {
3269
+ variant: "tertiary",
3270
+ onClick: onLoadMore,
3271
+ disabled: fetchingMore,
3272
+ isBusy: fetchingMore,
3273
+ children: fetchingMore ? __("Loading\u2026", "valu-block-components") : __("Load more", "valu-block-components")
3274
+ }
3275
+ ) }) : null,
3276
+ /* @__PURE__ */ jsx("div", { ref: sentinelRef, "aria-hidden": "true" })
3277
+ ]
3278
+ }
3279
+ )
3280
+ ] });
3281
+ };
3282
+ var MediaUpload = MediaUpload$1;
3283
+ var normalize = (raw, allowedMimeTypes, allowedExtensions) => {
3284
+ const list = Array.isArray(raw) ? raw : [raw];
3285
+ return list.map((r) => mapMediaToDocumentItem(r)).filter((item) => matchesAllowed(item, allowedMimeTypes, allowedExtensions));
3286
+ };
3287
+ var MediaLibraryButton = ({
3288
+ allowedMimeTypes,
3289
+ allowedExtensions,
3290
+ multiple,
3291
+ value,
3292
+ onSelect,
3293
+ labels,
3294
+ disabled,
3295
+ variant = "secondary",
3296
+ label
3297
+ }) => {
3298
+ const selectedIds = multiple ? value.map((v) => v.id) : value[0]?.id;
3299
+ const buttonLabel = label ?? labels.browseMediaLibrary ?? __("Browse Media Library", "valu-block-components");
3300
+ return /* @__PURE__ */ jsx(
3301
+ MediaUpload,
3302
+ {
3303
+ allowedTypes: allowedMimeTypes,
3304
+ multiple,
3305
+ gallery: false,
3306
+ ...selectedIds !== void 0 ? { value: selectedIds } : {},
3307
+ onSelect: (selection) => {
3308
+ const items = normalize(selection, allowedMimeTypes, allowedExtensions);
3309
+ if (items.length > 0) onSelect(items);
3310
+ },
3311
+ render: ({ open }) => /* @__PURE__ */ jsx(Button, { variant, icon: upload, onClick: open, disabled, children: buttonLabel })
3312
+ }
3313
+ );
3314
+ };
3315
+ var buildPath2 = (args) => {
3316
+ const params = {
3317
+ per_page: args.perPage,
3318
+ page: args.page,
3319
+ _fields: [
3320
+ "id",
3321
+ "title",
3322
+ "source_url",
3323
+ "mime_type",
3324
+ "media_details",
3325
+ "modified",
3326
+ "date"
3327
+ ].join(","),
3328
+ order: "desc"
3329
+ };
3330
+ const trimmed = args.query.trim();
3331
+ if (trimmed.length > 0) {
3332
+ params.search = trimmed;
3333
+ params.orderby = "relevance";
3334
+ } else {
3335
+ params.orderby = "date";
3336
+ }
3337
+ if (args.mimeTypes.length > 0) {
3338
+ params.mime_type = args.mimeTypes.join(",");
3339
+ }
3340
+ return addQueryArgs("/wp/v2/media", params);
3341
+ };
3342
+ var parseHeader = (response, key) => {
3343
+ const raw = response.headers.get(key);
3344
+ if (!raw) return 0;
3345
+ const parsed = parseInt(raw, 10);
3346
+ return Number.isNaN(parsed) ? 0 : parsed;
3347
+ };
3348
+ var fetchDocuments = async (args) => {
3349
+ const rawPath = buildPath2(args);
3350
+ const path = args.request ? args.request(rawPath, { query: args.query, page: args.page }) : rawPath;
3351
+ const response = await apiFetch({
3352
+ path,
3353
+ parse: false,
3354
+ signal: args.signal
3355
+ });
3356
+ const data = await response.json();
3357
+ const totalPages = parseHeader(response, "x-wp-totalpages") || 1;
3358
+ const total = parseHeader(response, "x-wp-total");
3359
+ const items = data.map(mapMediaToDocumentItem).filter((item) => matchesAllowed(item, args.mimeTypes, args.allowedExtensions));
3360
+ return {
3361
+ items,
3362
+ page: args.page,
3363
+ hasMore: args.page < totalPages,
3364
+ total
3365
+ };
3366
+ };
3367
+
3368
+ // components/document-picker/hooks/useDocumentSearch.ts
3369
+ var documentSearchKey = (args) => [
3370
+ "valu/document-search",
3371
+ [...args.mimeTypes].sort().join(","),
3372
+ [...args.allowedExtensions ?? []].sort().join(","),
3373
+ args.query,
3374
+ args.perPage
3375
+ ];
3376
+ var useDocumentSearch = (args) => useInfiniteQuery({
3377
+ queryKey: documentSearchKey(args),
3378
+ queryFn: ({ pageParam, signal }) => fetchDocuments({
3379
+ query: args.query,
3380
+ page: pageParam,
3381
+ perPage: args.perPage,
3382
+ mimeTypes: args.mimeTypes,
3383
+ ...args.allowedExtensions !== void 0 ? { allowedExtensions: args.allowedExtensions } : {},
3384
+ ...args.request !== void 0 ? { request: args.request } : {},
3385
+ signal
3386
+ }),
3387
+ initialPageParam: 1,
3388
+ getNextPageParam: (last) => last.hasMore ? last.page + 1 : void 0,
3389
+ enabled: args.enabled ?? true,
3390
+ staleTime: 3e4,
3391
+ placeholderData: keepPreviousData
3392
+ });
3393
+
3394
+ // components/document-picker/hooks/useDocumentPickerController.ts
3395
+ var useDocumentPickerController = (args) => {
3396
+ const {
3397
+ mimeTypes,
3398
+ allowedExtensions,
3399
+ perPage,
3400
+ inputDelayMs,
3401
+ uniqueItems,
3402
+ maxItems,
3403
+ filter,
3404
+ request,
3405
+ value,
3406
+ onChange,
3407
+ active,
3408
+ fetchEmpty,
3409
+ onSelectSideEffect,
3410
+ onEscape
3411
+ } = args;
3412
+ const [query, setQuery] = useState("");
3413
+ const debouncedQuery = useDebouncedValue(query, inputDelayMs);
3414
+ const enabled = active && (fetchEmpty || debouncedQuery.trim().length > 0);
3415
+ const query_ = useDocumentSearch({
3416
+ query: debouncedQuery,
3417
+ mimeTypes,
3418
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
3419
+ perPage,
3420
+ enabled,
3421
+ ...request !== void 0 ? { request } : {}
3422
+ });
3423
+ const suggestions = useMemo(() => {
3424
+ if (!query_.data) return [];
3425
+ const flat = query_.data.pages.flatMap((p) => p.items);
3426
+ let filtered = dedupeAgainst2(flat, value, uniqueItems);
3427
+ if (filter) {
3428
+ filtered = filter(filtered, { query: debouncedQuery });
3429
+ }
3430
+ return filtered;
3431
+ }, [query_.data, value, uniqueItems, filter, debouncedQuery]);
3432
+ const { announce, announceImmediate } = useLiveRegion();
3433
+ useEffect(() => {
3434
+ if (!active) return;
3435
+ if (debouncedQuery.trim().length === 0 && !fetchEmpty) return;
3436
+ if (query_.isFetching) return;
3437
+ if (query_.isError) {
3438
+ announce(__("Search failed. Try again.", "valu-block-components"));
3439
+ return;
3440
+ }
3441
+ const count = suggestions.length;
3442
+ announce(
3443
+ sprintf(
3444
+ /* translators: %d: number of search results */
3445
+ _n("%d document found", "%d documents found", count, "valu-block-components"),
3446
+ count
3447
+ )
3448
+ );
3449
+ }, [
3450
+ suggestions.length,
3451
+ query_.isFetching,
3452
+ query_.isError,
3453
+ active,
3454
+ debouncedQuery,
3455
+ fetchEmpty,
3456
+ announce
3457
+ ]);
3458
+ const maxedOut = isMaxedOut2(value, maxItems);
3459
+ const replace = useCallback((items) => onChange(items), [onChange]);
3460
+ const selectMany = useCallback(
3461
+ (items) => {
3462
+ if (items.length === 0) return;
3463
+ const limited = maxItems === 1;
3464
+ const normalized = items.map((item) => ({
3465
+ ...item,
3466
+ key: item.key && item.key.length > 0 ? item.key : generateKey2()
3467
+ }));
3468
+ if (limited) {
3469
+ const next2 = [normalized[normalized.length - 1]];
3470
+ onChange(next2);
3471
+ announceImmediate(
3472
+ sprintf(
3473
+ /* translators: %s: document title */
3474
+ __("Selected %s.", "valu-block-components"),
3475
+ next2[0].title
3476
+ )
3477
+ );
3478
+ onSelectSideEffect?.();
3479
+ return;
3480
+ }
3481
+ const existingIds = new Set(value.map((v) => v.id));
3482
+ const remaining = Number.isFinite(maxItems) ? Math.max(0, maxItems - value.length) : normalized.length;
3483
+ const toAdd = (uniqueItems ? normalized.filter((n) => !existingIds.has(n.id)) : normalized).slice(0, remaining);
3484
+ if (toAdd.length === 0) return;
3485
+ const next = [...value, ...toAdd];
3486
+ onChange(next);
3487
+ announceImmediate(
3488
+ sprintf(
3489
+ /* translators: 1: number added, 2: total selected */
3490
+ _n(
3491
+ "Added %1$d document. %2$d selected.",
3492
+ "Added %1$d documents. %2$d selected.",
3493
+ toAdd.length,
3494
+ "valu-block-components"
3495
+ ),
3496
+ toAdd.length,
3497
+ next.length
3498
+ )
3499
+ );
3500
+ onSelectSideEffect?.();
3501
+ },
3502
+ [maxItems, value, uniqueItems, onChange, announceImmediate, onSelectSideEffect]
3503
+ );
3504
+ const select = useCallback(
3505
+ (item) => {
3506
+ if (maxedOut && maxItems !== 1) return;
3507
+ selectMany([item]);
3508
+ },
3509
+ [maxedOut, maxItems, selectMany]
3510
+ );
3511
+ const remove = useCallback(
3512
+ (key) => {
3513
+ const target = value.find((i) => i.key === key);
3514
+ const next = value.filter((i) => i.key !== key);
3515
+ onChange(next);
3516
+ if (target) {
3517
+ announceImmediate(
3518
+ sprintf(
3519
+ /* translators: %s: title of the removed document */
3520
+ __("Removed %s.", "valu-block-components"),
3521
+ target.title
3522
+ )
3523
+ );
3524
+ }
3525
+ },
3526
+ [value, onChange, announceImmediate]
3527
+ );
3528
+ const reorder = useCallback(
3529
+ (from, to) => {
3530
+ if (from === to || from < 0 || to < 0 || from >= value.length || to >= value.length) {
3531
+ return;
3532
+ }
3533
+ const next = value.slice();
3534
+ const [moved] = next.splice(from, 1);
3535
+ next.splice(to, 0, moved);
3536
+ onChange(next);
3537
+ announceImmediate(
3538
+ sprintf(
3539
+ /* translators: 1: title, 2: position, 3: total */
3540
+ __("Moved %1$s to position %2$d of %3$d.", "valu-block-components"),
3541
+ moved.title,
3542
+ to + 1,
3543
+ next.length
3544
+ )
3545
+ );
3546
+ },
3547
+ [value, onChange, announceImmediate]
3548
+ );
3549
+ const {
3550
+ highlightedIndex,
3551
+ setHighlightedIndex,
3552
+ reset: resetHighlight,
3553
+ handleKeyDown
3554
+ } = useKeyboardNav({
3555
+ itemCount: suggestions.length,
3556
+ onSelect: (i) => {
3557
+ const item = suggestions[i];
3558
+ if (item) select(item);
3559
+ },
3560
+ ...onEscape !== void 0 ? { onEscape } : {}
3561
+ });
3562
+ useEffect(() => {
3563
+ resetHighlight();
3564
+ }, [debouncedQuery, resetHighlight]);
3565
+ return {
3566
+ query,
3567
+ setQuery,
3568
+ debouncedQuery,
3569
+ suggestions,
3570
+ highlightedIndex,
3571
+ setHighlightedIndex,
3572
+ handleKeyDown,
3573
+ loading: query_.isFetching && !query_.data,
3574
+ error: query_.error,
3575
+ hasMore: Boolean(query_.hasNextPage),
3576
+ loadMore: () => {
3577
+ if (query_.hasNextPage && !query_.isFetchingNextPage) void query_.fetchNextPage();
3578
+ },
3579
+ fetchingMore: query_.isFetchingNextPage,
3580
+ select,
3581
+ selectMany,
3582
+ remove,
3583
+ reorder,
3584
+ replace,
3585
+ maxedOut,
3586
+ total: query_.data?.pages[0]?.total ?? 0
3587
+ };
3588
+ };
3589
+ var DocumentPickerPopover = (props) => {
3590
+ const {
3591
+ rootId,
3592
+ label,
3593
+ hideLabelFromVision,
3594
+ help,
3595
+ placeholder,
3596
+ allowedMimeTypes,
3597
+ allowedExtensions,
3598
+ perPage,
3599
+ inputDelayMs,
3600
+ uniqueItems,
3601
+ maxItems,
3602
+ fetchOnOpen,
3603
+ enableMediaLibrary,
3604
+ showExtensionBadge,
3605
+ showFileSize,
3606
+ showModifiedDate,
3607
+ filter,
3608
+ request,
3609
+ renderSuggestion,
3610
+ value,
3611
+ onChange,
3612
+ labels
3613
+ } = props;
3614
+ const [open, setOpen] = useState(false);
3615
+ const inputRef = useRef(null);
3616
+ const popoverBodyRef = useRef(null);
3617
+ const clearQueryRef = useRef(null);
3618
+ const controller = useDocumentPickerController({
3619
+ mimeTypes: allowedMimeTypes,
3620
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
3621
+ perPage,
3622
+ inputDelayMs,
3623
+ uniqueItems,
3624
+ maxItems,
3625
+ ...filter !== void 0 ? { filter } : {},
3626
+ ...request !== void 0 ? { request } : {},
3627
+ value,
3628
+ onChange,
3629
+ active: open,
3630
+ fetchEmpty: fetchOnOpen,
3631
+ onSelectSideEffect: () => {
3632
+ if (maxItems === 1) {
3633
+ setOpen(false);
3634
+ return;
3635
+ }
3636
+ clearQueryRef.current?.();
3637
+ requestAnimationFrame(() => inputRef.current?.focus());
3638
+ },
3639
+ onEscape: () => {
3640
+ setOpen(false);
3641
+ requestAnimationFrame(() => inputRef.current?.focus());
3642
+ }
3643
+ });
3644
+ clearQueryRef.current = () => controller.setQuery("");
3645
+ const listboxId = aria2(rootId, "listbox");
3646
+ const activeOptionId = controller.highlightedIndex >= 0 ? aria2(rootId, `option-${controller.highlightedIndex}`) : void 0;
3647
+ const selectedKeys = useMemo(() => new Set(value.map((v) => v.key)), [value]);
3648
+ const handleFocus = useCallback(() => {
3649
+ if (!controller.maxedOut) setOpen(true);
3650
+ }, [controller.maxedOut]);
3651
+ useEffect(() => {
3652
+ if (controller.maxedOut) setOpen(false);
3653
+ }, [controller.maxedOut]);
3654
+ useEffect(() => {
3655
+ if (!open) return;
3656
+ const handlePointerDown = (event) => {
3657
+ const target = event.target;
3658
+ if (!target) return;
3659
+ const input = inputRef.current;
3660
+ const body = popoverBodyRef.current;
3661
+ const anchorContainer = input?.closest("div") ?? input;
3662
+ if (anchorContainer && anchorContainer.contains(target)) return;
3663
+ if (body && body.contains(target)) return;
3664
+ setOpen(false);
3665
+ };
3666
+ const docs = /* @__PURE__ */ new Set();
3667
+ if (typeof document !== "undefined") docs.add(document);
3668
+ const inputDoc = inputRef.current?.ownerDocument;
3669
+ if (inputDoc) docs.add(inputDoc);
3670
+ docs.forEach((d) => d.addEventListener("mousedown", handlePointerDown, true));
3671
+ return () => {
3672
+ docs.forEach((d) => d.removeEventListener("mousedown", handlePointerDown, true));
3673
+ };
3674
+ }, [open]);
3675
+ const disabled = controller.maxedOut;
3676
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
3677
+ /* @__PURE__ */ jsx(
3678
+ SearchCombobox2,
3679
+ {
3680
+ ref: inputRef,
3681
+ rootId,
3682
+ value: controller.query,
3683
+ onChange: (next) => {
3684
+ controller.setQuery(next);
3685
+ if (!open && !disabled) setOpen(true);
3686
+ },
3687
+ onFocus: handleFocus,
3688
+ onKeyDown: (event) => {
3689
+ if (event.key === "ArrowDown" && !open && !disabled) {
3690
+ event.preventDefault();
3691
+ setOpen(true);
3692
+ return;
3693
+ }
3694
+ controller.handleKeyDown(event);
3695
+ },
3696
+ ...placeholder !== void 0 ? { placeholder } : {},
3697
+ label,
3698
+ hideLabelFromVision,
3699
+ ...help !== void 0 ? { help } : {},
3700
+ disabled,
3701
+ open: open && !disabled,
3702
+ listboxId,
3703
+ ...activeOptionId !== void 0 ? { activeOptionId } : {},
3704
+ labels
3705
+ }
3706
+ ),
3707
+ enableMediaLibrary ? /* @__PURE__ */ jsx(ActionsRow, { children: /* @__PURE__ */ jsx(
3708
+ MediaLibraryButton,
3709
+ {
3710
+ allowedMimeTypes,
3711
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
3712
+ multiple: maxItems !== 1,
3713
+ value,
3714
+ onSelect: (items) => controller.selectMany(items),
3715
+ labels,
3716
+ disabled
3717
+ }
3718
+ ) }) : null,
3719
+ disabled ? /* @__PURE__ */ jsx(MaxedOutBanner, { role: "status", style: { marginTop: 8 }, children: labels.limitReached ?? __("Selection limit reached.", "valu-block-components") }) : null,
3720
+ open && !disabled && inputRef.current ? /* @__PURE__ */ jsx(
3721
+ Popover,
3722
+ {
3723
+ anchor: inputRef.current.closest("div") ?? inputRef.current,
3724
+ placement: "bottom-start",
3725
+ focusOnMount: false,
3726
+ onClose: () => setOpen(false),
3727
+ onFocusOutside: () => setOpen(false),
3728
+ shift: true,
3729
+ flip: true,
3730
+ resize: true,
3731
+ offset: 4,
3732
+ className: "valu-document-picker__popover",
3733
+ children: /* @__PURE__ */ jsx(EmotionCacheProvider, { children: /* @__PURE__ */ jsx(
3734
+ "div",
3735
+ {
3736
+ ref: popoverBodyRef,
3737
+ style: {
3738
+ minWidth: inputRef.current.offsetWidth,
3739
+ width: "100%"
3740
+ },
3741
+ children: /* @__PURE__ */ jsx(
3742
+ SuggestionList2,
3743
+ {
3744
+ listboxId,
3745
+ rootId,
3746
+ suggestions: controller.suggestions,
3747
+ loading: controller.loading,
3748
+ fetchingMore: controller.fetchingMore,
3749
+ hasMore: controller.hasMore,
3750
+ error: controller.error,
3751
+ query: controller.debouncedQuery,
3752
+ highlightedIndex: controller.highlightedIndex,
3753
+ selectedKeys,
3754
+ labels,
3755
+ showExtensionBadge,
3756
+ showFileSize,
3757
+ showModifiedDate,
3758
+ ...renderSuggestion ? { renderSuggestion } : {},
3759
+ onHover: (i) => controller.setHighlightedIndex(i),
3760
+ onSelect: (item) => controller.select(item),
3761
+ onLoadMore: () => controller.loadMore()
3762
+ }
3763
+ )
3764
+ }
3765
+ ) })
3766
+ }
3767
+ ) : null
3768
+ ] });
3769
+ };
3770
+ var DocumentPickerInline = (props) => {
3771
+ const {
3772
+ rootId,
3773
+ label,
3774
+ hideLabelFromVision,
3775
+ help,
3776
+ placeholder,
3777
+ allowedMimeTypes,
3778
+ allowedExtensions,
3779
+ perPage,
3780
+ inputDelayMs,
3781
+ uniqueItems,
3782
+ maxItems,
3783
+ fetchOnOpen,
3784
+ enableMediaLibrary,
3785
+ showExtensionBadge,
3786
+ showFileSize,
3787
+ showModifiedDate,
3788
+ filter,
3789
+ request,
3790
+ renderSuggestion,
3791
+ value,
3792
+ onChange,
3793
+ labels
3794
+ } = props;
3795
+ const inputRef = useRef(null);
3796
+ const clearQueryRef = useRef(null);
3797
+ const controller = useDocumentPickerController({
3798
+ mimeTypes: allowedMimeTypes,
3799
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
3800
+ perPage,
3801
+ inputDelayMs,
3802
+ uniqueItems,
3803
+ maxItems,
3804
+ ...filter !== void 0 ? { filter } : {},
3805
+ ...request !== void 0 ? { request } : {},
3806
+ value,
3807
+ onChange,
3808
+ active: true,
3809
+ fetchEmpty: fetchOnOpen || value.length === 0,
3810
+ onSelectSideEffect: () => {
3811
+ clearQueryRef.current?.();
3812
+ requestAnimationFrame(() => inputRef.current?.focus());
3813
+ }
3814
+ });
3815
+ clearQueryRef.current = () => controller.setQuery("");
3816
+ const listboxId = aria2(rootId, "listbox");
3817
+ const activeOptionId = controller.highlightedIndex >= 0 ? aria2(rootId, `option-${controller.highlightedIndex}`) : void 0;
3818
+ const selectedKeys = useMemo(() => new Set(value.map((v) => v.key)), [value]);
3819
+ const disabled = controller.maxedOut;
3820
+ const shouldShowList = !disabled && (controller.debouncedQuery.trim().length > 0 || fetchOnOpen || value.length === 0);
3821
+ return /* @__PURE__ */ jsxs("div", { children: [
3822
+ /* @__PURE__ */ jsx(
3823
+ SearchCombobox2,
3824
+ {
3825
+ ref: inputRef,
3826
+ rootId,
3827
+ value: controller.query,
3828
+ onChange: (next) => controller.setQuery(next),
3829
+ onKeyDown: controller.handleKeyDown,
3830
+ ...placeholder !== void 0 ? { placeholder } : {},
3831
+ label,
3832
+ hideLabelFromVision,
3833
+ ...help !== void 0 ? { help } : {},
3834
+ disabled,
3835
+ open: shouldShowList,
3836
+ listboxId,
3837
+ ...activeOptionId !== void 0 ? { activeOptionId } : {},
3838
+ labels
3839
+ }
3840
+ ),
3841
+ enableMediaLibrary ? /* @__PURE__ */ jsx(ActionsRow, { children: /* @__PURE__ */ jsx(
3842
+ MediaLibraryButton,
3843
+ {
3844
+ allowedMimeTypes,
3845
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
3846
+ multiple: maxItems !== 1,
3847
+ value,
3848
+ onSelect: (items) => controller.selectMany(items),
3849
+ labels,
3850
+ disabled
3851
+ }
3852
+ ) }) : null,
3853
+ disabled ? /* @__PURE__ */ jsx(MaxedOutBanner, { role: "status", style: { marginTop: 8 }, children: labels.limitReached ?? __("Selection limit reached.", "valu-block-components") }) : null,
3854
+ shouldShowList ? /* @__PURE__ */ jsx(SuggestionsShell, { inline: true, style: { marginTop: 8 }, children: /* @__PURE__ */ jsx(
3855
+ SuggestionList2,
3856
+ {
3857
+ listboxId,
3858
+ rootId,
3859
+ suggestions: controller.suggestions,
3860
+ loading: controller.loading,
3861
+ fetchingMore: controller.fetchingMore,
3862
+ hasMore: controller.hasMore,
3863
+ error: controller.error,
3864
+ query: controller.debouncedQuery,
3865
+ highlightedIndex: controller.highlightedIndex,
3866
+ selectedKeys,
3867
+ labels,
3868
+ showExtensionBadge,
3869
+ showFileSize,
3870
+ showModifiedDate,
3871
+ ...renderSuggestion ? { renderSuggestion } : {},
3872
+ onHover: (i) => controller.setHighlightedIndex(i),
3873
+ onSelect: (item) => controller.select(item),
3874
+ onLoadMore: () => controller.loadMore()
3875
+ }
3876
+ ) }) : null
3877
+ ] });
3878
+ };
3879
+ var SelectedItem2 = ({
3880
+ item,
3881
+ index,
3882
+ total,
3883
+ orderable,
3884
+ showExtensionBadge,
3885
+ showFileSize,
3886
+ showModifiedDate,
3887
+ labels,
3888
+ onRemove,
3889
+ onMoveUp,
3890
+ onMoveDown,
3891
+ render
3892
+ }) => {
3893
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
3894
+ id: item.key,
3895
+ disabled: !orderable
3896
+ });
3897
+ const style = {
3898
+ transform: CSS.Translate.toString(transform),
3899
+ transition,
3900
+ opacity: isDragging ? 0 : 1
3901
+ };
3902
+ const isMissing = item.missing === "deleted";
3903
+ const missingLabel = labels.missing ?? __("This document is no longer available.", "valu-block-components");
3904
+ const removeLabel = labels.remove ? labels.remove : sprintf(
3905
+ /* translators: %s: document title */
3906
+ __("Remove %s", "valu-block-components"),
3907
+ item.title
3908
+ );
3909
+ const moveUpLabel = labels.moveUp ? labels.moveUp : sprintf(
3910
+ /* translators: %s: document title */
3911
+ __("Move %s up", "valu-block-components"),
3912
+ item.title
3913
+ );
3914
+ const moveDownLabel = labels.moveDown ? labels.moveDown : sprintf(
3915
+ /* translators: %s: document title */
3916
+ __("Move %s down", "valu-block-components"),
3917
+ item.title
3918
+ );
3919
+ const modified = showModifiedDate ? formatModified(item.modified) : "";
3920
+ return /* @__PURE__ */ jsxs(
3921
+ SelectedRow2,
3922
+ {
3923
+ ref: setNodeRef,
3924
+ style,
3925
+ dragging: isDragging,
3926
+ missing: isMissing,
3927
+ "aria-setsize": total,
3928
+ "aria-posinset": index + 1,
3929
+ children: [
3930
+ orderable ? /* @__PURE__ */ jsx(
3931
+ DragHandleButton,
3932
+ {
3933
+ type: "button",
3934
+ "aria-label": labels.dragHandle ?? __("Reorder item", "valu-block-components"),
3935
+ ...attributes,
3936
+ ...listeners,
3937
+ children: /* @__PURE__ */ jsx(Icon, { icon: dragHandle, size: 14 })
3938
+ }
3939
+ ) : null,
3940
+ isMissing ? /* @__PURE__ */ jsx(Icon, { icon: caution, size: 20 }) : showExtensionBadge ? /* @__PURE__ */ jsx(ExtensionBadge, { extension: item.extension }) : null,
3941
+ render ? render(item) : /* @__PURE__ */ jsxs(Fragment, { children: [
3942
+ /* @__PURE__ */ jsxs(DocRowBody, { children: [
3943
+ /* @__PURE__ */ jsx(PrimaryText, { muted: isMissing, title: item.title, children: isMissing ? missingLabel : item.title }),
3944
+ !isMissing ? /* @__PURE__ */ jsx(FilenameText, { title: item.filename, children: item.filename }) : null
3945
+ ] }),
3946
+ !isMissing && (showFileSize || modified) ? /* @__PURE__ */ jsxs(DocMetaInline, { children: [
3947
+ showFileSize && item.fileSize ? /* @__PURE__ */ jsx("span", { children: formatFileSize(item.fileSize) }) : null,
3948
+ modified ? /* @__PURE__ */ jsx("span", { children: modified }) : null
3949
+ ] }) : null
3950
+ ] }),
3951
+ /* @__PURE__ */ jsxs(ActionColumn, { children: [
3952
+ orderable ? /* @__PURE__ */ jsxs(Fragment, { children: [
3953
+ /* @__PURE__ */ jsx(
3954
+ Button,
3955
+ {
3956
+ size: "small",
3957
+ variant: "tertiary",
3958
+ icon: chevronUp,
3959
+ label: moveUpLabel,
3960
+ onClick: onMoveUp,
3961
+ disabled: index === 0 || !onMoveUp
3962
+ }
3963
+ ),
3964
+ /* @__PURE__ */ jsx(
3965
+ Button,
3966
+ {
3967
+ size: "small",
3968
+ variant: "tertiary",
3969
+ icon: chevronDown,
3970
+ label: moveDownLabel,
3971
+ onClick: onMoveDown,
3972
+ disabled: index === total - 1 || !onMoveDown
3973
+ }
3974
+ )
3975
+ ] }) : null,
3976
+ /* @__PURE__ */ jsx(
3977
+ Button,
3978
+ {
3979
+ size: "small",
3980
+ variant: "tertiary",
3981
+ icon: closeSmall,
3982
+ label: removeLabel,
3983
+ onClick: onRemove,
3984
+ isDestructive: true
3985
+ }
3986
+ )
3987
+ ] })
3988
+ ]
3989
+ }
3990
+ );
3991
+ };
3992
+ var useResolvedDocuments = (value) => {
3993
+ const records = useSelect(
3994
+ (select) => {
3995
+ const core = select(store);
3996
+ const out = {};
3997
+ for (const item of value) {
3998
+ const record = core.getEntityRecord("postType", "attachment", item.id);
3999
+ const resolved = core.hasFinishedResolution("getEntityRecord", [
4000
+ "postType",
4001
+ "attachment",
4002
+ item.id
4003
+ ]);
4004
+ out[item.id] = { record: record ?? null, resolved };
4005
+ }
4006
+ return out;
4007
+ },
4008
+ [value]
4009
+ );
4010
+ return useMemo(
4011
+ () => value.map((item) => {
4012
+ const entry = records[item.id];
4013
+ if (!entry) return item;
4014
+ const { record, resolved } = entry;
4015
+ if (!resolved) return item;
4016
+ if (!record) return { ...item, missing: "deleted" };
4017
+ const fresh = mapMediaToDocumentItem(
4018
+ record
4019
+ );
4020
+ return {
4021
+ ...item,
4022
+ title: fresh.title || item.title,
4023
+ filename: fresh.filename || item.filename,
4024
+ url: fresh.url || item.url,
4025
+ mimeType: fresh.mimeType || item.mimeType,
4026
+ extension: fresh.extension || item.extension,
4027
+ fileSize: fresh.fileSize ?? item.fileSize,
4028
+ modified: fresh.modified ?? item.modified
4029
+ };
4030
+ }),
4031
+ [value, records]
4032
+ );
4033
+ };
4034
+ var SelectedList2 = ({
4035
+ value,
4036
+ orderable,
4037
+ maxItems,
4038
+ labels,
4039
+ onChange,
4040
+ renderSelected,
4041
+ showHeader = true,
4042
+ showExtensionBadge,
4043
+ showFileSize,
4044
+ showModifiedDate
4045
+ }) => {
4046
+ const resolved = useResolvedDocuments(value);
4047
+ const [activeKey, setActiveKey] = useState(null);
4048
+ const sensors = useSensors(
4049
+ useSensor(MouseSensor, { activationConstraint: { distance: 5 } }),
4050
+ useSensor(TouchSensor, { activationConstraint: { delay: 200, tolerance: 6 } }),
4051
+ useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
4052
+ );
4053
+ const ids = useMemo(() => resolved.map((i) => i.key), [resolved]);
4054
+ const activeItem = activeKey ? resolved.find((i) => i.key === activeKey) : null;
4055
+ const remove = useCallback(
4056
+ (key) => onChange(value.filter((i) => i.key !== key)),
4057
+ [value, onChange]
4058
+ );
4059
+ const reorder = useCallback(
4060
+ (from, to) => {
4061
+ if (from === to) return;
4062
+ onChange(arrayMove(value, from, to));
4063
+ },
4064
+ [value, onChange]
4065
+ );
4066
+ const handleDragStart = useCallback(
4067
+ (event) => setActiveKey(String(event.active.id)),
4068
+ []
4069
+ );
4070
+ const handleDragEnd = useCallback(
4071
+ (event) => {
4072
+ setActiveKey(null);
4073
+ const { active, over } = event;
4074
+ if (!over || active.id === over.id) return;
4075
+ const from = ids.indexOf(String(active.id));
4076
+ const to = ids.indexOf(String(over.id));
4077
+ if (from >= 0 && to >= 0) reorder(from, to);
4078
+ },
4079
+ [ids, reorder]
4080
+ );
4081
+ if (resolved.length === 0) return null;
4082
+ const isSingleSelect = maxItems === 1;
4083
+ const shouldShowHeader = showHeader && !isSingleSelect;
4084
+ const header = shouldShowHeader ? /* @__PURE__ */ jsxs(HeaderRow, { children: [
4085
+ /* @__PURE__ */ jsx(HeaderTitle, { children: resolved.length === 1 ? labels.selectedSingular ?? __("Selected", "valu-block-components") : labels.selectedPlural ?? __("Selected", "valu-block-components") }),
4086
+ /* @__PURE__ */ jsx(CountPill, { "aria-hidden": "true", children: Number.isFinite(maxItems) ? sprintf(
4087
+ /* translators: 1: current count, 2: max count */
4088
+ "%1$d / %2$d",
4089
+ resolved.length,
4090
+ maxItems
4091
+ ) : String(resolved.length) })
4092
+ ] }) : null;
4093
+ const rows = resolved.map((item, index) => /* @__PURE__ */ jsx(
4094
+ SelectedItem2,
4095
+ {
4096
+ item,
4097
+ index,
4098
+ total: resolved.length,
4099
+ orderable,
4100
+ showExtensionBadge,
4101
+ showFileSize,
4102
+ showModifiedDate,
4103
+ labels,
4104
+ onRemove: () => remove(item.key),
4105
+ ...index > 0 ? { onMoveUp: () => reorder(index, index - 1) } : {},
4106
+ ...index < resolved.length - 1 ? { onMoveDown: () => reorder(index, index + 1) } : {},
4107
+ ...renderSelected ? { render: renderSelected } : {}
4108
+ },
4109
+ item.key
4110
+ ));
4111
+ if (!orderable) {
4112
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4113
+ header,
4114
+ /* @__PURE__ */ jsx(SelectedListEl, { role: "list", children: rows })
4115
+ ] });
4116
+ }
4117
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4118
+ header,
4119
+ /* @__PURE__ */ jsxs(
4120
+ DndContext,
4121
+ {
4122
+ sensors,
4123
+ collisionDetection: closestCenter,
4124
+ onDragStart: handleDragStart,
4125
+ onDragEnd: handleDragEnd,
4126
+ onDragCancel: () => setActiveKey(null),
4127
+ children: [
4128
+ /* @__PURE__ */ jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx(SelectedListEl, { role: "list", children: rows }) }),
4129
+ /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: defaultDropAnimation, children: activeItem ? /* @__PURE__ */ jsx(DragChip, { title: activeItem.title }) : null })
4130
+ ]
4131
+ }
4132
+ )
4133
+ ] });
4134
+ };
4135
+ var DEFAULT_LABELS2 = {};
4136
+ var DEFAULT_MIME = [...DEFAULT_DOCUMENT_MIME_TYPES];
4137
+ var makeQueryClient2 = () => new QueryClient({
4138
+ defaultOptions: {
4139
+ queries: { retry: 1, refetchOnWindowFocus: false }
4140
+ }
4141
+ });
4142
+ var DocumentPicker = ({
4143
+ label,
4144
+ hideLabelFromVision = false,
4145
+ help,
4146
+ variant = "popover",
4147
+ value: controlledValue,
4148
+ defaultValue,
4149
+ onChange,
4150
+ maxItems = Infinity,
4151
+ orderable,
4152
+ placeholder,
4153
+ allowedMimeTypes,
4154
+ allowedExtensions,
4155
+ perPage,
4156
+ inputDelayMs = 250,
4157
+ fetchOnOpen = false,
4158
+ uniqueItems = true,
4159
+ enableMediaLibrary = true,
4160
+ showExtensionBadge = true,
4161
+ showFileSize = true,
4162
+ showModifiedDate = false,
4163
+ filter,
4164
+ request,
4165
+ renderSuggestion,
4166
+ renderSelected,
4167
+ labels = DEFAULT_LABELS2,
4168
+ className,
4169
+ id
4170
+ }) => {
4171
+ const reactId = useId();
4172
+ const rootId = id ?? `valu-dp-${reactId.replace(/:/g, "")}`;
4173
+ const [uncontrolled, setUncontrolled] = useState(defaultValue ?? []);
4174
+ const isControlled = controlledValue !== void 0;
4175
+ const value = isControlled ? controlledValue : uncontrolled;
4176
+ const handleChange = useCallback(
4177
+ (next) => {
4178
+ if (!isControlled) setUncontrolled(next);
4179
+ onChange?.(next);
4180
+ },
4181
+ [isControlled, onChange]
4182
+ );
4183
+ const effectiveMime = allowedMimeTypes ?? DEFAULT_MIME;
4184
+ const effectivePerPage = perPage ?? (variant === "popover" ? 10 : 20);
4185
+ const effectiveOrderable = orderable ?? (Number.isFinite(maxItems) ? maxItems > 1 : true);
4186
+ const queryClientRef = useRef(null);
4187
+ if (!queryClientRef.current) queryClientRef.current = makeQueryClient2();
4188
+ const isSingleSelect = maxItems === 1;
4189
+ const isFull = Number.isFinite(maxItems) && value.length >= maxItems;
4190
+ const hideInputForSingleSelect = isSingleSelect && value.length > 0;
4191
+ const showInput = !isFull && !hideInputForSingleSelect;
4192
+ const variantProps = useMemo(
4193
+ () => ({
4194
+ rootId,
4195
+ label,
4196
+ hideLabelFromVision,
4197
+ ...help !== void 0 ? { help } : {},
4198
+ ...placeholder !== void 0 ? { placeholder } : {},
4199
+ allowedMimeTypes: effectiveMime,
4200
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
4201
+ perPage: effectivePerPage,
4202
+ inputDelayMs,
4203
+ uniqueItems,
4204
+ maxItems,
4205
+ fetchOnOpen,
4206
+ enableMediaLibrary,
4207
+ showExtensionBadge,
4208
+ showFileSize,
4209
+ showModifiedDate,
4210
+ ...filter !== void 0 ? { filter } : {},
4211
+ ...request !== void 0 ? { request } : {},
4212
+ ...renderSuggestion ? { renderSuggestion } : {},
4213
+ value,
4214
+ onChange: handleChange,
4215
+ labels
4216
+ }),
4217
+ [
4218
+ rootId,
4219
+ label,
4220
+ hideLabelFromVision,
4221
+ help,
4222
+ placeholder,
4223
+ effectiveMime,
4224
+ allowedExtensions,
4225
+ effectivePerPage,
4226
+ inputDelayMs,
4227
+ uniqueItems,
4228
+ maxItems,
4229
+ fetchOnOpen,
4230
+ enableMediaLibrary,
4231
+ showExtensionBadge,
4232
+ showFileSize,
4233
+ showModifiedDate,
4234
+ filter,
4235
+ request,
4236
+ renderSuggestion,
4237
+ value,
4238
+ handleChange,
4239
+ labels
4240
+ ]
4241
+ );
4242
+ return /* @__PURE__ */ jsx(EmotionCacheProvider, { children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClientRef.current, children: /* @__PURE__ */ jsxs(Root3, { id: rootId, ...className !== void 0 ? { className } : {}, children: [
4243
+ showInput ? variant === "popover" ? /* @__PURE__ */ jsx(DocumentPickerPopover, { ...variantProps }) : /* @__PURE__ */ jsx(DocumentPickerInline, { ...variantProps }) : null,
4244
+ hideInputForSingleSelect && enableMediaLibrary ? /* @__PURE__ */ jsx(ActionsRow, { children: /* @__PURE__ */ jsx(
4245
+ MediaLibraryButton,
4246
+ {
4247
+ allowedMimeTypes: effectiveMime,
4248
+ ...allowedExtensions !== void 0 ? { allowedExtensions } : {},
4249
+ multiple: false,
4250
+ value,
4251
+ onSelect: (items) => handleChange([items[items.length - 1]]),
4252
+ labels,
4253
+ variant: "tertiary",
4254
+ label: labels.replace
4255
+ }
4256
+ ) }) : null,
4257
+ /* @__PURE__ */ jsx(
4258
+ SelectedList2,
4259
+ {
4260
+ value,
4261
+ orderable: effectiveOrderable && value.length > 1,
4262
+ maxItems,
4263
+ labels,
4264
+ onChange: handleChange,
4265
+ ...renderSelected ? { renderSelected } : {},
4266
+ showHeader: value.length > 0,
4267
+ showExtensionBadge,
4268
+ showFileSize,
4269
+ showModifiedDate
4270
+ }
4271
+ )
4272
+ ] }) }) });
4273
+ };
4274
+ var FileSize = ({ bytes }) => {
4275
+ const formatted = formatFileSize(bytes);
4276
+ if (!formatted) return null;
4277
+ return /* @__PURE__ */ jsx(FileMeta, { children: formatted });
4278
+ };
2715
4279
 
2716
- export { ContentPicker, Matrix, cellKey, selectionByRow, toggleCell };
4280
+ export { ContentPicker, DEFAULT_DOCUMENT_MIME_TYPES, DocumentPicker, ExtensionBadge, FileSize, Matrix, MediaLibraryButton, basenameFromUrl, cellKey, extensionFromUrl, formatFileSize, mapMediaToDocumentItem, selectionByRow, toggleCell };
2717
4281
  //# sourceMappingURL=index.js.map
2718
4282
  //# sourceMappingURL=index.js.map