@valu/block-components 0.2.1 → 0.3.1

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