@valu/block-components 0.2.1 → 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
 
@@ -2739,7 +2740,1543 @@ var Matrix = (props) => {
2739
2740
  )) })
2740
2741
  ] }) }) }) });
2741
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
+ };
2742
4279
 
2743
- 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 };
2744
4281
  //# sourceMappingURL=index.js.map
2745
4282
  //# sourceMappingURL=index.js.map