@burdenoff/microfe-files 2026.527.1 → 2026.527.3
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/files/components/ActionMenu.js +44 -40
- package/dist/files/components/ActionMenu.js.map +1 -1
- package/dist/files/components/ConfirmDialog.js +42 -40
- package/dist/files/components/ConfirmDialog.js.map +1 -1
- package/dist/files/components/CopyDialog.js +1 -0
- package/dist/files/components/CopyDialog.js.map +1 -1
- package/dist/files/components/FileBrowser.js +186 -170
- package/dist/files/components/FileBrowser.js.map +1 -1
- package/dist/files/components/FileCard.js.map +1 -1
- package/dist/files/components/FileInfoPanel.js +102 -102
- package/dist/files/components/FileInfoPanel.js.map +1 -1
- package/dist/files/components/FileListItem.js.map +1 -1
- package/dist/files/components/FolderCard.js +147 -161
- package/dist/files/components/FolderCard.js.map +1 -1
- package/dist/files/components/MoveDialog.js +9 -12
- package/dist/files/components/MoveDialog.js.map +1 -1
- package/dist/files/components/SearchBar.js +125 -117
- package/dist/files/components/SearchBar.js.map +1 -1
- package/dist/files/components/ShareDialog.js +5 -0
- package/dist/files/components/ShareDialog.js.map +1 -1
- package/dist/files/components/Toast.js +27 -24
- package/dist/files/components/Toast.js.map +1 -1
- package/dist/files/components/UploadQueue.js +1 -1
- package/dist/files/components/UploadQueue.js.map +1 -1
- package/dist/files/components/viewers/AudioPlayer.js +3 -0
- package/dist/files/components/viewers/AudioPlayer.js.map +1 -1
- package/dist/files/components/viewers/TextViewer.js +1 -0
- package/dist/files/components/viewers/TextViewer.js.map +1 -1
- package/dist/files/components/viewers/VideoPlayer.js +3 -0
- package/dist/files/components/viewers/VideoPlayer.js.map +1 -1
- package/dist/files/hooks/useCreateFolder.js +15 -7
- package/dist/files/hooks/useCreateFolder.js.map +1 -1
- package/dist/files/hooks/useKeyboardNavigation.js +51 -51
- package/dist/files/hooks/useKeyboardNavigation.js.map +1 -1
- package/dist/files/hooks/useMoveFile.js +1 -2
- package/dist/files/hooks/useMoveFile.js.map +1 -1
- package/dist/files/hooks/useMoveFolder.js +0 -2
- package/dist/files/hooks/useMoveFolder.js.map +1 -1
- package/dist/files/hooks/useStarredItems.js +4 -3
- package/dist/files/hooks/useStarredItems.js.map +1 -1
- package/dist/files/hooks/useToggleStar.js +10 -10
- package/dist/files/hooks/useToggleStar.js.map +1 -1
- package/dist/files/pages/BrowserPage.js +312 -302
- package/dist/files/pages/BrowserPage.js.map +1 -1
- package/dist/files/pages/ShareAccessPage.js +1 -0
- package/dist/files/pages/ShareAccessPage.js.map +1 -1
- package/dist/files/utils/quotaUtils.js +14 -0
- package/dist/files/utils/quotaUtils.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { tWithFallback as e } from "../utils/i18n.js";
|
|
2
|
-
import { useCallback as t, useEffect as n,
|
|
3
|
-
import { Filter as
|
|
4
|
-
import { jsx as
|
|
5
|
-
import { useI18n as
|
|
2
|
+
import { useCallback as t, useEffect as n, useEffectEvent as r, useState as i } from "react";
|
|
3
|
+
import { Filter as a, Search as o, X as s } from "lucide-react";
|
|
4
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
5
|
+
import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
6
|
//#region src/files/components/SearchBar.tsx
|
|
7
|
-
var
|
|
7
|
+
var d = {
|
|
8
8
|
image: [
|
|
9
9
|
"image/jpeg",
|
|
10
10
|
"image/png",
|
|
@@ -37,217 +37,225 @@ var u = {
|
|
|
37
37
|
"application/x-rar-compressed"
|
|
38
38
|
]
|
|
39
39
|
};
|
|
40
|
-
function
|
|
41
|
-
let { t:
|
|
40
|
+
function f({ onSearch: f, placeholder: p = "Search files and folders...", initialQuery: m = "", showFilters: h = !0 }) {
|
|
41
|
+
let { t: g } = u(), [_, v] = i(m), [y, b] = i(!1), [x, S] = i({}), C = x.status, w = x.status, T = x.visibility, E = x.fileType ? d[x.fileType] : void 0;
|
|
42
|
+
n(() => {
|
|
43
|
+
v(m);
|
|
44
|
+
}, [m]);
|
|
45
|
+
let D = r(() => {
|
|
46
|
+
(_.length >= 2 || _.length === 0) && f(_, {
|
|
47
|
+
fileFilter: {
|
|
48
|
+
...E && { mimeTypes: E },
|
|
49
|
+
...C && { status: C },
|
|
50
|
+
...T && { visibility: T },
|
|
51
|
+
...x.starred !== void 0 && { starred: x.starred }
|
|
52
|
+
},
|
|
53
|
+
folderFilter: {
|
|
54
|
+
...w && { status: w },
|
|
55
|
+
...x.starred !== void 0 && { starred: x.starred }
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
42
59
|
n(() => {
|
|
43
|
-
_(p);
|
|
44
|
-
}, [p]), n(() => {
|
|
45
60
|
let e = setTimeout(() => {
|
|
46
|
-
(
|
|
47
|
-
fileFilter: {
|
|
48
|
-
...T && { mimeTypes: T },
|
|
49
|
-
...S && { status: S },
|
|
50
|
-
...w && { visibility: w },
|
|
51
|
-
...b.starred !== void 0 && { starred: b.starred }
|
|
52
|
-
},
|
|
53
|
-
folderFilter: {
|
|
54
|
-
...C && { status: C },
|
|
55
|
-
...b.starred !== void 0 && { starred: b.starred }
|
|
56
|
-
}
|
|
57
|
-
});
|
|
61
|
+
D();
|
|
58
62
|
}, 300);
|
|
59
63
|
return () => clearTimeout(e);
|
|
60
64
|
}, [
|
|
61
|
-
|
|
65
|
+
C,
|
|
66
|
+
E,
|
|
62
67
|
T,
|
|
63
68
|
w,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
d,
|
|
67
|
-
g
|
|
69
|
+
x,
|
|
70
|
+
_
|
|
68
71
|
]);
|
|
69
|
-
let
|
|
70
|
-
|
|
71
|
-
}, [
|
|
72
|
-
return /* @__PURE__ */
|
|
72
|
+
let O = t(() => {
|
|
73
|
+
v(""), S({}), f("");
|
|
74
|
+
}, [f]), k = Object.values(x).filter((e) => e !== void 0).length;
|
|
75
|
+
return /* @__PURE__ */ l("div", {
|
|
73
76
|
className: "relative",
|
|
74
|
-
children: [/* @__PURE__ */
|
|
77
|
+
children: [/* @__PURE__ */ l("div", {
|
|
75
78
|
className: "relative flex items-center",
|
|
76
79
|
children: [
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
/* @__PURE__ */
|
|
80
|
+
/* @__PURE__ */ c(o, { className: "absolute left-3 size-4 text-text-tertiary" }),
|
|
81
|
+
/* @__PURE__ */ c("label", {
|
|
79
82
|
htmlFor: "files-search-input",
|
|
80
83
|
className: "sr-only",
|
|
81
|
-
children: e(
|
|
84
|
+
children: e(g, "files.search.input.label", "Search files and folders")
|
|
82
85
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
86
|
+
/* @__PURE__ */ c("input", {
|
|
84
87
|
id: "files-search-input",
|
|
85
88
|
type: "text",
|
|
86
|
-
value:
|
|
87
|
-
onChange: (e) =>
|
|
88
|
-
placeholder:
|
|
89
|
+
value: _,
|
|
90
|
+
onChange: (e) => v(e.target.value),
|
|
91
|
+
placeholder: p,
|
|
92
|
+
"aria-label": e(g, "files.search.input.label", "Search files and folders"),
|
|
89
93
|
className: "w-full pl-[3.25rem] pr-20 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border focus:border-action-primary-border transition-colors duration-200"
|
|
90
94
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ l("div", {
|
|
92
96
|
className: "absolute right-2 flex items-center gap-1",
|
|
93
|
-
children: [
|
|
97
|
+
children: [_ && /* @__PURE__ */ c("button", {
|
|
94
98
|
type: "button",
|
|
95
|
-
onClick:
|
|
99
|
+
onClick: O,
|
|
96
100
|
className: "p-1.5 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
|
|
97
|
-
title: e(
|
|
98
|
-
"aria-label": e(
|
|
99
|
-
children: /* @__PURE__ */ s
|
|
100
|
-
}),
|
|
101
|
+
title: e(g, "files.search.actions.clearSearch", "Clear search"),
|
|
102
|
+
"aria-label": e(g, "files.search.actions.clearSearch", "Clear search"),
|
|
103
|
+
children: /* @__PURE__ */ c(s, { className: "size-4" })
|
|
104
|
+
}), h && /* @__PURE__ */ l("button", {
|
|
101
105
|
type: "button",
|
|
102
|
-
onClick: () =>
|
|
103
|
-
"aria-expanded":
|
|
106
|
+
onClick: () => b(!y),
|
|
107
|
+
"aria-expanded": y,
|
|
104
108
|
"aria-controls": "files-search-filter-panel",
|
|
105
|
-
"aria-label": e(
|
|
106
|
-
className: `p-1.5 rounded transition-colors duration-200 relative ${
|
|
107
|
-
title: e(
|
|
108
|
-
children: [/* @__PURE__ */
|
|
109
|
+
"aria-label": e(g, "files.search.filters.toggleAria", "Toggle filters"),
|
|
110
|
+
className: `p-1.5 rounded transition-colors duration-200 relative ${y || k > 0 ? "text-text-link bg-bg-accent hover:bg-action-primary-bg/20" : "text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover"}`,
|
|
111
|
+
title: e(g, "files.search.filters.title", "Filters"),
|
|
112
|
+
children: [/* @__PURE__ */ c(a, { className: "size-4" }), k > 0 && /* @__PURE__ */ c("span", {
|
|
109
113
|
className: "absolute -top-1 -right-1 size-4 bg-action-primary-bg text-text-inverse text-[10px] font-bold rounded-full flex items-center justify-center",
|
|
110
|
-
children:
|
|
114
|
+
children: k
|
|
111
115
|
})]
|
|
112
116
|
})]
|
|
113
117
|
})
|
|
114
118
|
]
|
|
115
|
-
}),
|
|
119
|
+
}), y && /* @__PURE__ */ c("div", {
|
|
116
120
|
id: "files-search-filter-panel",
|
|
117
121
|
className: "absolute top-full left-0 right-0 mt-2 p-4 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10 animate-in fade-in duration-200",
|
|
118
|
-
children: /* @__PURE__ */
|
|
122
|
+
children: /* @__PURE__ */ l("div", {
|
|
119
123
|
className: "space-y-4",
|
|
120
124
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
|
|
122
126
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
123
|
-
children: e(
|
|
124
|
-
}), /* @__PURE__ */
|
|
125
|
-
value:
|
|
126
|
-
onChange: (e) =>
|
|
127
|
-
...
|
|
127
|
+
children: e(g, "files.search.filters.fileType.label", "File Type")
|
|
128
|
+
}), /* @__PURE__ */ l("select", {
|
|
129
|
+
value: x.fileType || "",
|
|
130
|
+
onChange: (e) => S({
|
|
131
|
+
...x,
|
|
128
132
|
fileType: e.target.value || void 0
|
|
129
133
|
}),
|
|
134
|
+
"aria-label": e(g, "files.search.filters.fileType.label", "File Type"),
|
|
130
135
|
className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200",
|
|
131
136
|
children: [
|
|
132
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ c("option", {
|
|
133
138
|
value: "",
|
|
134
|
-
children: e(
|
|
139
|
+
children: e(g, "files.search.filters.fileType.options.all", "All types")
|
|
135
140
|
}),
|
|
136
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ c("option", {
|
|
137
142
|
value: "image",
|
|
138
|
-
children: e(
|
|
143
|
+
children: e(g, "files.search.filters.fileType.options.image", "Images")
|
|
139
144
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
145
|
+
/* @__PURE__ */ c("option", {
|
|
141
146
|
value: "video",
|
|
142
|
-
children: e(
|
|
147
|
+
children: e(g, "files.search.filters.fileType.options.video", "Videos")
|
|
143
148
|
}),
|
|
144
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ c("option", {
|
|
145
150
|
value: "document",
|
|
146
|
-
children: e(
|
|
151
|
+
children: e(g, "files.search.filters.fileType.options.document", "Documents")
|
|
147
152
|
}),
|
|
148
|
-
/* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ c("option", {
|
|
149
154
|
value: "pdf",
|
|
150
|
-
children: e(
|
|
155
|
+
children: e(g, "files.search.filters.fileType.options.pdf", "PDFs")
|
|
151
156
|
}),
|
|
152
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ c("option", {
|
|
153
158
|
value: "archive",
|
|
154
|
-
children: e(
|
|
159
|
+
children: e(g, "files.search.filters.fileType.options.archive", "Archives")
|
|
155
160
|
})
|
|
156
161
|
]
|
|
157
162
|
})] }),
|
|
158
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
|
|
159
164
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
160
|
-
children: e(
|
|
161
|
-
}), /* @__PURE__ */
|
|
162
|
-
value:
|
|
163
|
-
onChange: (e) =>
|
|
164
|
-
...
|
|
165
|
+
children: e(g, "files.search.filters.status.label", "Status")
|
|
166
|
+
}), /* @__PURE__ */ l("select", {
|
|
167
|
+
value: x.status || "",
|
|
168
|
+
onChange: (e) => S({
|
|
169
|
+
...x,
|
|
165
170
|
status: e.target.value || void 0
|
|
166
171
|
}),
|
|
172
|
+
"aria-label": e(g, "files.search.filters.status.label", "Status"),
|
|
167
173
|
className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200",
|
|
168
174
|
children: [
|
|
169
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ c("option", {
|
|
170
176
|
value: "",
|
|
171
|
-
children: e(
|
|
177
|
+
children: e(g, "files.search.filters.status.options.all", "All statuses")
|
|
172
178
|
}),
|
|
173
|
-
/* @__PURE__ */
|
|
179
|
+
/* @__PURE__ */ c("option", {
|
|
174
180
|
value: "ACTIVE",
|
|
175
|
-
children: e(
|
|
181
|
+
children: e(g, "files.search.filters.status.options.active", "Active")
|
|
176
182
|
}),
|
|
177
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ c("option", {
|
|
178
184
|
value: "ARCHIVED",
|
|
179
|
-
children: e(
|
|
185
|
+
children: e(g, "files.search.filters.status.options.archived", "Archived")
|
|
180
186
|
}),
|
|
181
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ c("option", {
|
|
182
188
|
value: "DELETED",
|
|
183
|
-
children: e(
|
|
189
|
+
children: e(g, "files.search.filters.status.options.deleted", "Deleted")
|
|
184
190
|
})
|
|
185
191
|
]
|
|
186
192
|
})] }),
|
|
187
|
-
/* @__PURE__ */
|
|
193
|
+
/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
|
|
188
194
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
189
|
-
children: e(
|
|
190
|
-
}), /* @__PURE__ */
|
|
191
|
-
value:
|
|
192
|
-
onChange: (e) =>
|
|
193
|
-
...
|
|
195
|
+
children: e(g, "files.search.filters.visibility.label", "Visibility")
|
|
196
|
+
}), /* @__PURE__ */ l("select", {
|
|
197
|
+
value: x.visibility || "",
|
|
198
|
+
onChange: (e) => S({
|
|
199
|
+
...x,
|
|
194
200
|
visibility: e.target.value || void 0
|
|
195
201
|
}),
|
|
202
|
+
"aria-label": e(g, "files.search.filters.visibility.label", "Visibility"),
|
|
196
203
|
className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200",
|
|
197
204
|
children: [
|
|
198
|
-
/* @__PURE__ */
|
|
205
|
+
/* @__PURE__ */ c("option", {
|
|
199
206
|
value: "",
|
|
200
|
-
children: e(
|
|
207
|
+
children: e(g, "files.search.filters.visibility.options.all", "All visibility")
|
|
201
208
|
}),
|
|
202
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ c("option", {
|
|
203
210
|
value: "PRIVATE",
|
|
204
|
-
children: e(
|
|
211
|
+
children: e(g, "files.search.filters.visibility.options.private", "Private")
|
|
205
212
|
}),
|
|
206
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ c("option", {
|
|
207
214
|
value: "TEAM",
|
|
208
|
-
children: e(
|
|
215
|
+
children: e(g, "files.search.filters.visibility.options.team", "Team")
|
|
209
216
|
}),
|
|
210
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ c("option", {
|
|
211
218
|
value: "WORKSPACE",
|
|
212
|
-
children: e(
|
|
219
|
+
children: e(g, "files.search.filters.visibility.options.workspace", "Workspace")
|
|
213
220
|
}),
|
|
214
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ c("option", {
|
|
215
222
|
value: "PUBLIC",
|
|
216
|
-
children: e(
|
|
223
|
+
children: e(g, "files.search.filters.visibility.options.public", "Public")
|
|
217
224
|
})
|
|
218
225
|
]
|
|
219
226
|
})] }),
|
|
220
|
-
/* @__PURE__ */
|
|
227
|
+
/* @__PURE__ */ l("div", {
|
|
221
228
|
className: "flex items-center",
|
|
222
|
-
children: [/* @__PURE__ */
|
|
229
|
+
children: [/* @__PURE__ */ c("input", {
|
|
223
230
|
type: "checkbox",
|
|
224
231
|
id: "starred-filter",
|
|
225
|
-
checked:
|
|
226
|
-
onChange: (e) =>
|
|
227
|
-
...
|
|
232
|
+
checked: x.starred || !1,
|
|
233
|
+
onChange: (e) => S({
|
|
234
|
+
...x,
|
|
228
235
|
starred: e.target.checked || void 0
|
|
229
236
|
}),
|
|
237
|
+
"aria-label": e(g, "files.search.filters.starred.label", "Starred only"),
|
|
230
238
|
className: "size-4 text-action-primary-bg border-border-default rounded focus:ring-action-primary-border transition-colors duration-200"
|
|
231
|
-
}), /* @__PURE__ */
|
|
239
|
+
}), /* @__PURE__ */ c("label", {
|
|
232
240
|
htmlFor: "starred-filter",
|
|
233
241
|
className: "ml-2 text-sm text-text-primary",
|
|
234
|
-
children: e(
|
|
242
|
+
children: e(g, "files.search.filters.starredOnly", "Starred only")
|
|
235
243
|
})]
|
|
236
244
|
}),
|
|
237
|
-
/* @__PURE__ */
|
|
245
|
+
/* @__PURE__ */ l("div", {
|
|
238
246
|
className: "flex justify-end gap-2 pt-2 border-t border-border-default",
|
|
239
|
-
children: [/* @__PURE__ */
|
|
247
|
+
children: [/* @__PURE__ */ c("button", {
|
|
240
248
|
type: "button",
|
|
241
249
|
onClick: () => {
|
|
242
|
-
|
|
250
|
+
S({}), b(!1);
|
|
243
251
|
},
|
|
244
252
|
className: "px-3 py-1.5 text-sm text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
|
|
245
|
-
children: e(
|
|
246
|
-
}), /* @__PURE__ */
|
|
253
|
+
children: e(g, "files.search.filters.actions.clear", "Clear filters")
|
|
254
|
+
}), /* @__PURE__ */ c("button", {
|
|
247
255
|
type: "button",
|
|
248
|
-
onClick: () =>
|
|
256
|
+
onClick: () => b(!1),
|
|
249
257
|
className: "px-3 py-1.5 text-sm bg-action-primary-bg text-text-inverse rounded hover:bg-action-primary-bg-hover transition-colors duration-200",
|
|
250
|
-
children: e(
|
|
258
|
+
children: e(g, "files.search.filters.actions.apply", "Apply")
|
|
251
259
|
})]
|
|
252
260
|
})
|
|
253
261
|
]
|
|
@@ -256,6 +264,6 @@ function d({ onSearch: d, placeholder: f = "Search files and folders...", initia
|
|
|
256
264
|
});
|
|
257
265
|
}
|
|
258
266
|
//#endregion
|
|
259
|
-
export {
|
|
267
|
+
export { f as SearchBar };
|
|
260
268
|
|
|
261
269
|
//# sourceMappingURL=SearchBar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.js","names":[],"sources":["../../../src/files/components/SearchBar.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { Search, X, Filter } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileFilterInput, FolderFilterInput } from \"../graphql-types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface SearchBarProps {\n onSearch: (\n query: string,\n filters?: {\n fileFilter?: FileFilterInput;\n folderFilter?: FolderFilterInput;\n },\n ) => void;\n placeholder?: string;\n initialQuery?: string;\n showFilters?: boolean;\n}\n\nconst FILE_TYPE_MIME_MAP: Record<string, string[]> = {\n image: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/svg+xml\",\n ],\n video: [\"video/mp4\", \"video/webm\", \"video/quicktime\"],\n document: [\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n \"application/json\",\n ],\n pdf: [\"application/pdf\"],\n archive: [\n \"application/zip\",\n \"application/x-tar\",\n \"application/x-7z-compressed\",\n \"application/gzip\",\n \"application/x-rar-compressed\",\n ],\n};\n\nexport function SearchBar({\n onSearch,\n placeholder = \"Search files and folders...\",\n initialQuery = \"\",\n showFilters = true,\n}: SearchBarProps) {\n const { t } = useI18n();\n const [query, setQuery] = useState(initialQuery);\n const [showFilterPanel, setShowFilterPanel] = useState(false);\n const [filters, setFilters] = useState<{\n fileType?: string;\n status?: string;\n visibility?: string;\n starred?: boolean;\n }>({});\n const fileStatus = filters.status as FileFilterInput[\"status\"] | undefined;\n const folderStatus = filters.status as\n | FolderFilterInput[\"status\"]\n | undefined;\n const fileVisibility = filters.visibility as\n | FileFilterInput[\"visibility\"]\n | undefined;\n const fileMimeTypes = filters.fileType\n ? FILE_TYPE_MIME_MAP[filters.fileType]\n : undefined;\n\n useEffect(() => {\n setQuery(initialQuery);\n }, [initialQuery]);\n\n // Debounced search\n useEffect(() => {\n const timer = setTimeout(() => {\n if (query.length >= 2 || query.length === 0) {\n const fileFilter: FileFilterInput = {\n ...(fileMimeTypes && { mimeTypes: fileMimeTypes }),\n ...(fileStatus && { status: fileStatus }),\n ...(fileVisibility && { visibility: fileVisibility }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n const folderFilter: FolderFilterInput = {\n ...(folderStatus && { status: folderStatus }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n onSearch(query, { fileFilter, folderFilter });\n }\n }, 300);\n\n return () => clearTimeout(timer);\n }, [\n fileStatus,\n fileMimeTypes,\n fileVisibility,\n folderStatus,\n filters,\n onSearch,\n query,\n ]);\n\n const handleClear = useCallback(() => {\n setQuery(\"\");\n setFilters({});\n onSearch(\"\");\n }, [onSearch]);\n\n const activeFilterCount = Object.values(filters).filter(\n (v) => v !== undefined,\n ).length;\n\n return (\n <div className=\"relative\">\n {/* Search Input */}\n <div className=\"relative flex items-center\">\n <Search className=\"absolute left-3 size-4 text-text-tertiary\" />\n <label htmlFor=\"files-search-input\" className=\"sr-only\">\n {tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n </label>\n <input\n id=\"files-search-input\"\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n className=\"w-full pl-[3.25rem] pr-20 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border focus:border-action-primary-border transition-colors duration-200\"\n />\n <div className=\"absolute right-2 flex items-center gap-1\">\n {query && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"p-1.5 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n aria-label={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n >\n <X className=\"size-4\" />\n </button>\n )}\n {showFilters && (\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(!showFilterPanel)}\n aria-expanded={showFilterPanel}\n aria-controls=\"files-search-filter-panel\"\n aria-label={tWithFallback(\n t,\n \"files.search.filters.toggleAria\",\n \"Toggle filters\",\n )}\n className={`p-1.5 rounded transition-colors duration-200 relative ${\n showFilterPanel || activeFilterCount > 0\n ? \"text-text-link bg-bg-accent hover:bg-action-primary-bg/20\"\n : \"text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n title={tWithFallback(t, \"files.search.filters.title\", \"Filters\")}\n >\n <Filter className=\"size-4\" />\n {activeFilterCount > 0 && (\n <span className=\"absolute -top-1 -right-1 size-4 bg-action-primary-bg text-text-inverse text-[10px] font-bold rounded-full flex items-center justify-center\">\n {activeFilterCount}\n </span>\n )}\n </button>\n )}\n </div>\n </div>\n\n {/* Filter Panel */}\n {showFilterPanel && (\n <div\n id=\"files-search-filter-panel\"\n className=\"absolute top-full left-0 right-0 mt-2 p-4 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10 animate-in fade-in duration-200\"\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n </label>\n <select\n value={filters.fileType || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n fileType: e.target.value || undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.all\",\n \"All types\",\n )}\n </option>\n <option value=\"image\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.image\",\n \"Images\",\n )}\n </option>\n <option value=\"video\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.video\",\n \"Videos\",\n )}\n </option>\n <option value=\"document\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.document\",\n \"Documents\",\n )}\n </option>\n <option value=\"pdf\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.pdf\",\n \"PDFs\",\n )}\n </option>\n <option value=\"archive\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.archive\",\n \"Archives\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n </label>\n <select\n value={filters.status || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n status: e.target.value || undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.all\",\n \"All statuses\",\n )}\n </option>\n <option value=\"ACTIVE\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.active\",\n \"Active\",\n )}\n </option>\n <option value=\"ARCHIVED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.archived\",\n \"Archived\",\n )}\n </option>\n <option value=\"DELETED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.deleted\",\n \"Deleted\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n </label>\n <select\n value={filters.visibility || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n visibility: e.target.value || undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.all\",\n \"All visibility\",\n )}\n </option>\n <option value=\"PRIVATE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.private\",\n \"Private\",\n )}\n </option>\n <option value=\"TEAM\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.team\",\n \"Team\",\n )}\n </option>\n <option value=\"WORKSPACE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.workspace\",\n \"Workspace\",\n )}\n </option>\n <option value=\"PUBLIC\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.public\",\n \"Public\",\n )}\n </option>\n </select>\n </div>\n\n <div className=\"flex items-center\">\n <input\n type=\"checkbox\"\n id=\"starred-filter\"\n checked={filters.starred || false}\n onChange={(e) =>\n setFilters({\n ...filters,\n starred: e.target.checked || undefined,\n })\n }\n className=\"size-4 text-action-primary-bg border-border-default rounded focus:ring-action-primary-border transition-colors duration-200\"\n />\n <label\n htmlFor=\"starred-filter\"\n className=\"ml-2 text-sm text-text-primary\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.starredOnly\",\n \"Starred only\",\n )}\n </label>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => {\n setFilters({});\n setShowFilterPanel(false);\n }}\n className=\"px-3 py-1.5 text-sm text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.clear\",\n \"Clear filters\",\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(false)}\n className=\"px-3 py-1.5 text-sm bg-action-primary-bg text-text-inverse rounded hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.apply\",\n \"Apply\",\n )}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SearchBar.js","names":[],"sources":["../../../src/files/components/SearchBar.tsx"],"sourcesContent":["import { useState, useEffect, useEffectEvent, useCallback } from \"react\";\nimport { Search, X, Filter } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileFilterInput, FolderFilterInput } from \"../graphql-types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface SearchBarProps {\n onSearch: (\n query: string,\n filters?: {\n fileFilter?: FileFilterInput;\n folderFilter?: FolderFilterInput;\n },\n ) => void;\n placeholder?: string;\n initialQuery?: string;\n showFilters?: boolean;\n}\n\nconst FILE_TYPE_MIME_MAP: Record<string, string[]> = {\n image: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/svg+xml\",\n ],\n video: [\"video/mp4\", \"video/webm\", \"video/quicktime\"],\n document: [\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n \"application/json\",\n ],\n pdf: [\"application/pdf\"],\n archive: [\n \"application/zip\",\n \"application/x-tar\",\n \"application/x-7z-compressed\",\n \"application/gzip\",\n \"application/x-rar-compressed\",\n ],\n};\n\nexport function SearchBar({\n onSearch,\n placeholder = \"Search files and folders...\",\n initialQuery = \"\",\n showFilters = true,\n}: SearchBarProps) {\n const { t } = useI18n();\n const [query, setQuery] = useState(initialQuery);\n const [showFilterPanel, setShowFilterPanel] = useState(false);\n const [filters, setFilters] = useState<{\n fileType?: string;\n status?: string;\n visibility?: string;\n starred?: boolean;\n }>({});\n const fileStatus = filters.status as FileFilterInput[\"status\"] | undefined;\n const folderStatus = filters.status as\n | FolderFilterInput[\"status\"]\n | undefined;\n const fileVisibility = filters.visibility as\n | FileFilterInput[\"visibility\"]\n | undefined;\n const fileMimeTypes = filters.fileType\n ? FILE_TYPE_MIME_MAP[filters.fileType]\n : undefined;\n\n useEffect(() => {\n setQuery(initialQuery);\n }, [initialQuery]);\n\n const runSearch = useEffectEvent(() => {\n if (query.length >= 2 || query.length === 0) {\n const fileFilter: FileFilterInput = {\n ...(fileMimeTypes && { mimeTypes: fileMimeTypes }),\n ...(fileStatus && { status: fileStatus }),\n ...(fileVisibility && { visibility: fileVisibility }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n const folderFilter: FolderFilterInput = {\n ...(folderStatus && { status: folderStatus }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n onSearch(query, { fileFilter, folderFilter });\n }\n });\n\n // Debounced search\n useEffect(() => {\n const timer = setTimeout(() => {\n runSearch();\n }, 300);\n\n return () => clearTimeout(timer);\n }, [fileStatus, fileMimeTypes, fileVisibility, folderStatus, filters, query]);\n\n const handleClear = useCallback(() => {\n setQuery(\"\");\n setFilters({});\n onSearch(\"\");\n }, [onSearch]);\n\n const activeFilterCount = Object.values(filters).filter(\n (v) => v !== undefined,\n ).length;\n\n return (\n <div className=\"relative\">\n {/* Search Input */}\n <div className=\"relative flex items-center\">\n <Search className=\"absolute left-3 size-4 text-text-tertiary\" />\n <label htmlFor=\"files-search-input\" className=\"sr-only\">\n {tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n </label>\n <input\n id=\"files-search-input\"\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n aria-label={tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n className=\"w-full pl-[3.25rem] pr-20 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border focus:border-action-primary-border transition-colors duration-200\"\n />\n <div className=\"absolute right-2 flex items-center gap-1\">\n {query && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"p-1.5 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n aria-label={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n >\n <X className=\"size-4\" />\n </button>\n )}\n {showFilters && (\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(!showFilterPanel)}\n aria-expanded={showFilterPanel}\n aria-controls=\"files-search-filter-panel\"\n aria-label={tWithFallback(\n t,\n \"files.search.filters.toggleAria\",\n \"Toggle filters\",\n )}\n className={`p-1.5 rounded transition-colors duration-200 relative ${\n showFilterPanel || activeFilterCount > 0\n ? \"text-text-link bg-bg-accent hover:bg-action-primary-bg/20\"\n : \"text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n title={tWithFallback(t, \"files.search.filters.title\", \"Filters\")}\n >\n <Filter className=\"size-4\" />\n {activeFilterCount > 0 && (\n <span className=\"absolute -top-1 -right-1 size-4 bg-action-primary-bg text-text-inverse text-[10px] font-bold rounded-full flex items-center justify-center\">\n {activeFilterCount}\n </span>\n )}\n </button>\n )}\n </div>\n </div>\n\n {/* Filter Panel */}\n {showFilterPanel && (\n <div\n id=\"files-search-filter-panel\"\n className=\"absolute top-full left-0 right-0 mt-2 p-4 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10 animate-in fade-in duration-200\"\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n </label>\n <select\n value={filters.fileType || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n fileType: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.all\",\n \"All types\",\n )}\n </option>\n <option value=\"image\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.image\",\n \"Images\",\n )}\n </option>\n <option value=\"video\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.video\",\n \"Videos\",\n )}\n </option>\n <option value=\"document\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.document\",\n \"Documents\",\n )}\n </option>\n <option value=\"pdf\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.pdf\",\n \"PDFs\",\n )}\n </option>\n <option value=\"archive\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.archive\",\n \"Archives\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n </label>\n <select\n value={filters.status || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n status: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.all\",\n \"All statuses\",\n )}\n </option>\n <option value=\"ACTIVE\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.active\",\n \"Active\",\n )}\n </option>\n <option value=\"ARCHIVED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.archived\",\n \"Archived\",\n )}\n </option>\n <option value=\"DELETED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.deleted\",\n \"Deleted\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n </label>\n <select\n value={filters.visibility || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n visibility: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.all\",\n \"All visibility\",\n )}\n </option>\n <option value=\"PRIVATE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.private\",\n \"Private\",\n )}\n </option>\n <option value=\"TEAM\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.team\",\n \"Team\",\n )}\n </option>\n <option value=\"WORKSPACE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.workspace\",\n \"Workspace\",\n )}\n </option>\n <option value=\"PUBLIC\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.public\",\n \"Public\",\n )}\n </option>\n </select>\n </div>\n\n <div className=\"flex items-center\">\n <input\n type=\"checkbox\"\n id=\"starred-filter\"\n checked={filters.starred || false}\n onChange={(e) =>\n setFilters({\n ...filters,\n starred: e.target.checked || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.starred.label\",\n \"Starred only\",\n )}\n className=\"size-4 text-action-primary-bg border-border-default rounded focus:ring-action-primary-border transition-colors duration-200\"\n />\n <label\n htmlFor=\"starred-filter\"\n className=\"ml-2 text-sm text-text-primary\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.starredOnly\",\n \"Starred only\",\n )}\n </label>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => {\n setFilters({});\n setShowFilterPanel(false);\n }}\n className=\"px-3 py-1.5 text-sm text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.clear\",\n \"Clear filters\",\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(false)}\n className=\"px-3 py-1.5 text-sm bg-action-primary-bg text-text-inverse rounded hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.apply\",\n \"Apply\",\n )}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -85,6 +85,7 @@ function g({ item: g, itemType: _, onClose: v, onCreateShare: y, isLoading: b })
|
|
|
85
85
|
min: "0",
|
|
86
86
|
value: A,
|
|
87
87
|
onChange: (e) => j(Number(e.target.value)),
|
|
88
|
+
"aria-label": e(x, "files.shareDialog.downloads.label", "Maximum downloads"),
|
|
88
89
|
placeholder: e(x, "files.shareDialog.downloads.placeholder", "Unlimited"),
|
|
89
90
|
className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200"
|
|
90
91
|
})] }),
|
|
@@ -99,6 +100,7 @@ function g({ item: g, itemType: _, onClose: v, onCreateShare: y, isLoading: b })
|
|
|
99
100
|
type: "checkbox",
|
|
100
101
|
checked: M,
|
|
101
102
|
onChange: (e) => N(e.target.checked),
|
|
103
|
+
"aria-label": e(x, "files.shareDialog.password.require", "Require password"),
|
|
102
104
|
className: "rounded transition-colors duration-200"
|
|
103
105
|
}), /* @__PURE__ */ p("span", {
|
|
104
106
|
className: "text-sm text-text-secondary",
|
|
@@ -109,6 +111,7 @@ function g({ item: g, itemType: _, onClose: v, onCreateShare: y, isLoading: b })
|
|
|
109
111
|
type: "password",
|
|
110
112
|
value: P,
|
|
111
113
|
onChange: (e) => F(e.target.value),
|
|
114
|
+
"aria-label": e(x, "files.shareDialog.password.label", "Password protection"),
|
|
112
115
|
placeholder: e(x, "files.shareDialog.password.placeholder", "Enter password"),
|
|
113
116
|
className: "w-full mt-2 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200"
|
|
114
117
|
}), /* @__PURE__ */ p("p", {
|
|
@@ -125,6 +128,7 @@ function g({ item: g, itemType: _, onClose: v, onCreateShare: y, isLoading: b })
|
|
|
125
128
|
type: "email",
|
|
126
129
|
value: I,
|
|
127
130
|
onChange: (e) => L(e.target.value),
|
|
131
|
+
"aria-label": e(x, "files.shareDialog.invite.label", "Invite specific person (optional)"),
|
|
128
132
|
placeholder: "user@example.com",
|
|
129
133
|
className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200"
|
|
130
134
|
}),
|
|
@@ -185,6 +189,7 @@ function g({ item: g, itemType: _, onClose: v, onCreateShare: y, isLoading: b })
|
|
|
185
189
|
type: "text",
|
|
186
190
|
value: S || "",
|
|
187
191
|
readOnly: !0,
|
|
192
|
+
"aria-label": e(x, "files.shareDialog.success.linkCreated", "Share link"),
|
|
188
193
|
className: "flex-1 px-3 py-2 bg-bg-surface border border-status-success-border rounded-lg text-sm"
|
|
189
194
|
}), /* @__PURE__ */ p("button", {
|
|
190
195
|
type: "button",
|