@keepkit/ui 0.5.0 → 0.6.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/README.md +2 -2
- package/dist/index.d.ts +63 -33
- package/dist/index.js +298 -195
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11,27 +11,49 @@ import {
|
|
|
11
11
|
} from "@keepkit/core/react";
|
|
12
12
|
import {
|
|
13
13
|
cloneElement,
|
|
14
|
-
createContext,
|
|
15
14
|
isValidElement,
|
|
16
15
|
useCallback,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
useMemo,
|
|
16
|
+
useEffect as useEffect2,
|
|
17
|
+
useMemo as useMemo2,
|
|
20
18
|
useRef,
|
|
21
|
-
useState
|
|
19
|
+
useState as useState2
|
|
22
20
|
} from "react";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
21
|
+
|
|
22
|
+
// src/KeepItemCheckbox.tsx
|
|
23
|
+
import { jsx } from "react/jsx-runtime";
|
|
24
|
+
function KeepItemCheckbox({
|
|
25
|
+
item,
|
|
26
|
+
checked = false,
|
|
27
|
+
label,
|
|
28
|
+
onCheckedChange,
|
|
29
|
+
"aria-label": ariaLabel,
|
|
30
|
+
...props
|
|
31
|
+
}) {
|
|
32
|
+
const itemLabel = getItemLabel(item) ?? item.id;
|
|
33
|
+
const accessibleLabel = ariaLabel ?? (typeof label === "string" ? label : itemLabel);
|
|
34
|
+
return /* @__PURE__ */ jsx(
|
|
35
|
+
"input",
|
|
36
|
+
{
|
|
37
|
+
...props,
|
|
38
|
+
type: "checkbox",
|
|
39
|
+
checked,
|
|
40
|
+
"aria-label": accessibleLabel,
|
|
41
|
+
onChange: (event) => onCheckedChange?.(event.currentTarget.checked)
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function getItemLabel(item) {
|
|
46
|
+
if (typeof item.meta !== "object" || item.meta === null || !("title" in item.meta)) return void 0;
|
|
47
|
+
const title = item.meta.title;
|
|
48
|
+
return typeof title === "string" && title.trim() ? title.trim() : void 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/query-controls.tsx
|
|
52
|
+
import { useEffect, useState } from "react";
|
|
53
|
+
|
|
54
|
+
// src/ui-context.tsx
|
|
55
|
+
import { createContext, useContext, useMemo } from "react";
|
|
56
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
35
57
|
var DEFAULT_LABELS = {
|
|
36
58
|
save: "Save",
|
|
37
59
|
saved: "Saved",
|
|
@@ -51,6 +73,10 @@ var DEFAULT_LABELS = {
|
|
|
51
73
|
sort: "Sort saved items",
|
|
52
74
|
newest: "Newest first",
|
|
53
75
|
oldest: "Oldest first",
|
|
76
|
+
savedNewest: "Saved newest first",
|
|
77
|
+
savedOldest: "Saved oldest first",
|
|
78
|
+
updatedNewest: "Updated newest first",
|
|
79
|
+
updatedOldest: "Updated oldest first",
|
|
54
80
|
previousPage: "Previous page",
|
|
55
81
|
nextPage: "Next page",
|
|
56
82
|
pagination: "Pagination",
|
|
@@ -70,8 +96,149 @@ function KeepUiProvider({ labels, locale, labelResolver, children }) {
|
|
|
70
96
|
const resolved = { ...DEFAULT_LABELS, ...labels };
|
|
71
97
|
return { locale, labels: resolved, labelResolver };
|
|
72
98
|
}, [labelResolver, labels, locale]);
|
|
73
|
-
return /* @__PURE__ */
|
|
99
|
+
return /* @__PURE__ */ jsx2(KeepUiLabelsContext.Provider, { value, children });
|
|
100
|
+
}
|
|
101
|
+
function useKeepUiLabels() {
|
|
102
|
+
return useContext(KeepUiLabelsContext);
|
|
103
|
+
}
|
|
104
|
+
function useUiLabel(key, override) {
|
|
105
|
+
const context = useKeepUiLabels();
|
|
106
|
+
return override ?? context.labelResolver?.(key, { locale: context.locale }) ?? context.labels[key];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/query-controls.tsx
|
|
110
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
111
|
+
function KeepSearchInput({
|
|
112
|
+
value: controlledValue,
|
|
113
|
+
defaultValue = "",
|
|
114
|
+
debounceMs = 300,
|
|
115
|
+
onValueChange,
|
|
116
|
+
"aria-label": ariaLabel,
|
|
117
|
+
placeholder,
|
|
118
|
+
...props
|
|
119
|
+
}) {
|
|
120
|
+
const label = useUiLabel("search");
|
|
121
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
122
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (!onValueChange) return;
|
|
125
|
+
if (debounceMs <= 0) {
|
|
126
|
+
onValueChange(value);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const timer = window.setTimeout(() => onValueChange(value), debounceMs);
|
|
130
|
+
return () => window.clearTimeout(timer);
|
|
131
|
+
}, [debounceMs, onValueChange, value]);
|
|
132
|
+
return /* @__PURE__ */ jsx3(
|
|
133
|
+
"input",
|
|
134
|
+
{
|
|
135
|
+
...props,
|
|
136
|
+
type: "search",
|
|
137
|
+
value,
|
|
138
|
+
"aria-label": ariaLabel ?? label,
|
|
139
|
+
placeholder: placeholder ?? label,
|
|
140
|
+
onChange: (event) => {
|
|
141
|
+
const nextValue = event.currentTarget.value;
|
|
142
|
+
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
function KeepSortSelect({
|
|
148
|
+
value: controlledValue,
|
|
149
|
+
defaultValue = "updatedAt:desc",
|
|
150
|
+
onValueChange,
|
|
151
|
+
"aria-label": ariaLabel,
|
|
152
|
+
children,
|
|
153
|
+
...props
|
|
154
|
+
}) {
|
|
155
|
+
const label = useUiLabel("sort");
|
|
156
|
+
const updatedNewestLabel = useUiLabel("updatedNewest");
|
|
157
|
+
const updatedOldestLabel = useUiLabel("updatedOldest");
|
|
158
|
+
const savedNewestLabel = useUiLabel("savedNewest");
|
|
159
|
+
const savedOldestLabel = useUiLabel("savedOldest");
|
|
160
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
161
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
162
|
+
const options = children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
163
|
+
/* @__PURE__ */ jsx3("option", { value: "updatedAt:desc", children: updatedNewestLabel }),
|
|
164
|
+
/* @__PURE__ */ jsx3("option", { value: "updatedAt:asc", children: updatedOldestLabel }),
|
|
165
|
+
/* @__PURE__ */ jsx3("option", { value: "savedAt:desc", children: savedNewestLabel }),
|
|
166
|
+
/* @__PURE__ */ jsx3("option", { value: "savedAt:asc", children: savedOldestLabel })
|
|
167
|
+
] });
|
|
168
|
+
return /* @__PURE__ */ jsx3(
|
|
169
|
+
"select",
|
|
170
|
+
{
|
|
171
|
+
...props,
|
|
172
|
+
value,
|
|
173
|
+
"aria-label": ariaLabel ?? label,
|
|
174
|
+
onChange: (event) => {
|
|
175
|
+
const nextValue = event.currentTarget.value;
|
|
176
|
+
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
177
|
+
const [by, direction] = nextValue.split(":");
|
|
178
|
+
onValueChange?.(nextValue, { by, direction });
|
|
179
|
+
},
|
|
180
|
+
children: options
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function KeepPagination({
|
|
185
|
+
totalCount,
|
|
186
|
+
pageSize,
|
|
187
|
+
page = 1,
|
|
188
|
+
maxPageButtons = 7,
|
|
189
|
+
onPageChange,
|
|
190
|
+
render,
|
|
191
|
+
...props
|
|
192
|
+
}) {
|
|
193
|
+
const previousPageLabel = useUiLabel("previousPage");
|
|
194
|
+
const nextPageLabel = useUiLabel("nextPage");
|
|
195
|
+
const pageLabel = useUiLabel("page");
|
|
196
|
+
const paginationLabel = useUiLabel("pagination");
|
|
197
|
+
const pageCount = Math.max(1, Math.ceil(totalCount / Math.max(1, pageSize)));
|
|
198
|
+
const currentPage = Math.min(Math.max(1, page), pageCount);
|
|
199
|
+
const goToPage = (nextPage) => {
|
|
200
|
+
const next = Math.min(Math.max(1, nextPage), pageCount);
|
|
201
|
+
onPageChange?.(next, (next - 1) * pageSize);
|
|
202
|
+
};
|
|
203
|
+
const navProps = { ...props, "aria-label": props["aria-label"] ?? paginationLabel };
|
|
204
|
+
if (render) return /* @__PURE__ */ jsx3("nav", { ...navProps, children: render({ page: currentPage, pageCount, goToPage }) });
|
|
205
|
+
const visiblePages = getVisiblePages(currentPage, pageCount, Math.max(1, maxPageButtons));
|
|
206
|
+
return /* @__PURE__ */ jsxs("nav", { ...navProps, children: [
|
|
207
|
+
/* @__PURE__ */ jsx3("button", { type: "button", onClick: () => goToPage(currentPage - 1), disabled: currentPage <= 1, children: previousPageLabel }),
|
|
208
|
+
visiblePages.map((nextPage) => /* @__PURE__ */ jsx3(
|
|
209
|
+
"button",
|
|
210
|
+
{
|
|
211
|
+
type: "button",
|
|
212
|
+
"aria-current": nextPage === currentPage ? "page" : void 0,
|
|
213
|
+
"aria-label": `${pageLabel} ${nextPage}`,
|
|
214
|
+
onClick: () => goToPage(nextPage),
|
|
215
|
+
children: nextPage
|
|
216
|
+
},
|
|
217
|
+
nextPage
|
|
218
|
+
)),
|
|
219
|
+
/* @__PURE__ */ jsx3("button", { type: "button", onClick: () => goToPage(currentPage + 1), disabled: currentPage >= pageCount, children: nextPageLabel })
|
|
220
|
+
] });
|
|
74
221
|
}
|
|
222
|
+
function getVisiblePages(currentPage, pageCount, maxPageButtons) {
|
|
223
|
+
if (pageCount <= maxPageButtons) return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
224
|
+
const half = Math.floor(maxPageButtons / 2);
|
|
225
|
+
const start = Math.min(Math.max(1, currentPage - half), pageCount - maxPageButtons + 1);
|
|
226
|
+
return Array.from({ length: maxPageButtons }, (_, index) => start + index);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/index.tsx
|
|
230
|
+
import { KeepProvider, useKeepContext as useKeepContext2, useKeepItem as useKeepItem2, useKeepList as useKeepList2, useKeepShortcut } from "@keepkit/core/react";
|
|
231
|
+
import {
|
|
232
|
+
createBrowserStorageAdapter,
|
|
233
|
+
createStorageAdapter,
|
|
234
|
+
FallbackStorageAdapter,
|
|
235
|
+
IndexedDBAdapter,
|
|
236
|
+
IndexedDBSyncQueueAdapter,
|
|
237
|
+
LocalStorageAdapter,
|
|
238
|
+
LocalStorageSyncQueueAdapter,
|
|
239
|
+
SyncStorageAdapter
|
|
240
|
+
} from "@keepkit/core/storage";
|
|
241
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
75
242
|
function KeepKitProvider({
|
|
76
243
|
labels,
|
|
77
244
|
locale,
|
|
@@ -79,20 +246,16 @@ function KeepKitProvider({
|
|
|
79
246
|
children,
|
|
80
247
|
...providerProps
|
|
81
248
|
}) {
|
|
82
|
-
return /* @__PURE__ */
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
function useUiLabel(key, override) {
|
|
88
|
-
const context = useKeepUiLabels();
|
|
89
|
-
return override ?? context.labelResolver?.(key, { locale: context.locale }) ?? context.labels[key];
|
|
249
|
+
return /* @__PURE__ */ jsx4(KeepUiProvider, { labels, locale, labelResolver, children: /* @__PURE__ */ jsxs2(CoreKeepProvider, { ...providerProps, children: [
|
|
250
|
+
children,
|
|
251
|
+
/* @__PURE__ */ jsx4(KeepAnnouncements, {})
|
|
252
|
+
] }) });
|
|
90
253
|
}
|
|
91
254
|
function createKeepKit(options = {}) {
|
|
92
255
|
const { labels, locale, labelResolver, getTitle, getImageProps, ...coreOptions } = options;
|
|
93
256
|
const coreKit = createCoreKeepKit(coreOptions);
|
|
94
257
|
return {
|
|
95
|
-
Provider: (props) => /* @__PURE__ */
|
|
258
|
+
Provider: (props) => /* @__PURE__ */ jsx4(
|
|
96
259
|
KeepKitProvider,
|
|
97
260
|
{
|
|
98
261
|
...coreOptions,
|
|
@@ -102,8 +265,8 @@ function createKeepKit(options = {}) {
|
|
|
102
265
|
...props
|
|
103
266
|
}
|
|
104
267
|
),
|
|
105
|
-
Button: (props) => /* @__PURE__ */
|
|
106
|
-
Collection: (props) => /* @__PURE__ */
|
|
268
|
+
Button: (props) => /* @__PURE__ */ jsx4(KeepButton, { ...props }),
|
|
269
|
+
Collection: (props) => /* @__PURE__ */ jsx4(
|
|
107
270
|
KeepCollection,
|
|
108
271
|
{
|
|
109
272
|
...props,
|
|
@@ -143,12 +306,12 @@ function KeepButton({ labels, ...props }) {
|
|
|
143
306
|
savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,
|
|
144
307
|
unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel
|
|
145
308
|
};
|
|
146
|
-
if (!customStateLabel) return /* @__PURE__ */
|
|
309
|
+
if (!customStateLabel) return /* @__PURE__ */ jsx4(CoreKeepButton, { ...sharedProps2 });
|
|
147
310
|
const statefulProps2 = {
|
|
148
311
|
...sharedProps2,
|
|
149
|
-
children: (state) => /* @__PURE__ */
|
|
312
|
+
children: (state) => /* @__PURE__ */ jsx4(Fragment2, { children: getStateContent(state) })
|
|
150
313
|
};
|
|
151
|
-
return /* @__PURE__ */
|
|
314
|
+
return /* @__PURE__ */ jsx4(CoreKeepButton, { ...statefulProps2 });
|
|
152
315
|
}
|
|
153
316
|
const sharedProps = {
|
|
154
317
|
...props,
|
|
@@ -158,9 +321,9 @@ function KeepButton({ labels, ...props }) {
|
|
|
158
321
|
savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,
|
|
159
322
|
unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel
|
|
160
323
|
};
|
|
161
|
-
if (!customStateLabel) return /* @__PURE__ */
|
|
324
|
+
if (!customStateLabel) return /* @__PURE__ */ jsx4(CoreKeepButton, { ...sharedProps });
|
|
162
325
|
const statefulProps = { ...sharedProps, children: (state) => getStateContent(state) };
|
|
163
|
-
return /* @__PURE__ */
|
|
326
|
+
return /* @__PURE__ */ jsx4(CoreKeepButton, { ...statefulProps });
|
|
164
327
|
}
|
|
165
328
|
function KeepItemCard({
|
|
166
329
|
item,
|
|
@@ -202,10 +365,10 @@ function KeepItemCard({
|
|
|
202
365
|
onRemoveError?.(error);
|
|
203
366
|
}
|
|
204
367
|
}
|
|
205
|
-
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */
|
|
206
|
-
imageProps ? renderImage?.({ ...imageProps, alt: imageAlt ?? imageProps.alt }, item) ?? (ImageComponent ? /* @__PURE__ */
|
|
207
|
-
/* @__PURE__ */
|
|
208
|
-
showSaveButton ? /* @__PURE__ */
|
|
368
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
369
|
+
imageProps ? renderImage?.({ ...imageProps, alt: imageAlt ?? imageProps.alt }, item) ?? (ImageComponent ? /* @__PURE__ */ jsx4(ImageComponent, { ...imageProps, alt: imageAlt ?? imageProps.alt }) : /* @__PURE__ */ jsx4("img", { ...imageProps, alt: imageAlt ?? imageProps.alt })) : null,
|
|
370
|
+
/* @__PURE__ */ jsx4("h3", { children: resolvedTitle }),
|
|
371
|
+
showSaveButton ? /* @__PURE__ */ jsx4(
|
|
209
372
|
KeepButton,
|
|
210
373
|
{
|
|
211
374
|
item: toKeepButtonItem(item),
|
|
@@ -213,7 +376,7 @@ function KeepItemCard({
|
|
|
213
376
|
getAriaLabel: (buttonState) => `${buttonState.isSaved ? removeActionLabel : saveActionLabel} ${String(resolvedTitle)}`
|
|
214
377
|
}
|
|
215
378
|
) : null,
|
|
216
|
-
/* @__PURE__ */
|
|
379
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: () => void handleRemove(), disabled: itemState.isMutating, children: removeLabel ?? removeActionLabel })
|
|
217
380
|
] });
|
|
218
381
|
return renderRoot(
|
|
219
382
|
asChild,
|
|
@@ -263,9 +426,9 @@ function getListBody(state, options) {
|
|
|
263
426
|
if (typeof options.children === "function") return options.children(state);
|
|
264
427
|
if (options.children !== void 0 && !isValidElement(options.children)) return options.children;
|
|
265
428
|
const items = state.items.map(
|
|
266
|
-
(item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */
|
|
429
|
+
(item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
|
|
267
430
|
);
|
|
268
|
-
return /* @__PURE__ */
|
|
431
|
+
return /* @__PURE__ */ jsx4("ul", { children: items });
|
|
269
432
|
}
|
|
270
433
|
function KeepCollection({
|
|
271
434
|
query = {},
|
|
@@ -287,12 +450,12 @@ function KeepCollection({
|
|
|
287
450
|
bulkActions: false,
|
|
288
451
|
...features
|
|
289
452
|
};
|
|
290
|
-
const [searchValue, setSearchValue] =
|
|
291
|
-
const [sort, setSort] =
|
|
292
|
-
const [tag, setTag] =
|
|
293
|
-
const [page, setPage] =
|
|
453
|
+
const [searchValue, setSearchValue] = useState2(query.search?.query ?? "");
|
|
454
|
+
const [sort, setSort] = useState2(query.sort ?? { by: "updatedAt", direction: "desc" });
|
|
455
|
+
const [tag, setTag] = useState2(query.tags?.[0]);
|
|
456
|
+
const [page, setPage] = useState2(query.pagination?.page ?? 1);
|
|
294
457
|
const resolvedPageSize = query.pagination?.pageSize ?? pageSize;
|
|
295
|
-
const resolvedQuery =
|
|
458
|
+
const resolvedQuery = useMemo2(
|
|
296
459
|
() => ({
|
|
297
460
|
...query,
|
|
298
461
|
search: enabled.search ? { ...query.search, query: searchValue } : query.search,
|
|
@@ -303,24 +466,24 @@ function KeepCollection({
|
|
|
303
466
|
[enabled.pagination, enabled.search, enabled.sort, page, query, resolvedPageSize, searchValue, sort, tag]
|
|
304
467
|
);
|
|
305
468
|
const list = useKeepList(resolvedQuery);
|
|
306
|
-
|
|
469
|
+
useEffect2(() => {
|
|
307
470
|
if (searchValue !== void 0 || sort.by !== void 0 || sort.direction !== void 0 || tag !== void 0) {
|
|
308
471
|
setPage(1);
|
|
309
472
|
}
|
|
310
473
|
}, [searchValue, sort.by, sort.direction, tag]);
|
|
311
|
-
return /* @__PURE__ */
|
|
474
|
+
return /* @__PURE__ */ jsxs2(
|
|
312
475
|
"section",
|
|
313
476
|
{
|
|
314
477
|
...rootProps,
|
|
315
478
|
className,
|
|
316
479
|
"aria-busy": list.isLoading || list.isMutating || rootProps["aria-busy"],
|
|
317
480
|
children: [
|
|
318
|
-
/* @__PURE__ */
|
|
319
|
-
enabled.search ? /* @__PURE__ */
|
|
320
|
-
enabled.sort ? /* @__PURE__ */
|
|
321
|
-
enabled.tagFilter ? /* @__PURE__ */
|
|
481
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
482
|
+
enabled.search ? /* @__PURE__ */ jsx4(KeepSearchInput, { value: searchValue, onValueChange: setSearchValue }) : null,
|
|
483
|
+
enabled.sort ? /* @__PURE__ */ jsx4(KeepSortSelect, { value: sortToValue(sort), onValueChange: (_value, nextSort) => setSort(nextSort) }) : null,
|
|
484
|
+
enabled.tagFilter ? /* @__PURE__ */ jsx4(KeepTagFilter, { query, value: tag, onValueChange: setTag }) : null
|
|
322
485
|
] }),
|
|
323
|
-
/* @__PURE__ */
|
|
486
|
+
/* @__PURE__ */ jsx4(
|
|
324
487
|
KeepList,
|
|
325
488
|
{
|
|
326
489
|
query: resolvedQuery,
|
|
@@ -331,7 +494,7 @@ function KeepCollection({
|
|
|
331
494
|
error
|
|
332
495
|
}
|
|
333
496
|
),
|
|
334
|
-
enabled.pagination ? /* @__PURE__ */
|
|
497
|
+
enabled.pagination ? /* @__PURE__ */ jsx4(
|
|
335
498
|
KeepPagination,
|
|
336
499
|
{
|
|
337
500
|
totalCount: list.totalCount,
|
|
@@ -340,8 +503,7 @@ function KeepCollection({
|
|
|
340
503
|
onPageChange: (_nextPage, nextPageOffset) => setPage(Math.floor(nextPageOffset / resolvedPageSize) + 1)
|
|
341
504
|
}
|
|
342
505
|
) : null,
|
|
343
|
-
enabled.bulkActions ? /* @__PURE__ */
|
|
344
|
-
/* @__PURE__ */ jsx(KeepAnnouncements, {})
|
|
506
|
+
enabled.bulkActions ? /* @__PURE__ */ jsx4(KeepBulkActions, { query: resolvedQuery }) : null
|
|
345
507
|
]
|
|
346
508
|
}
|
|
347
509
|
);
|
|
@@ -366,7 +528,7 @@ function KeepTagFilter({
|
|
|
366
528
|
const defaultAllLabel = allLabel ?? uiAllLabel;
|
|
367
529
|
const defaultAriaLabel = ariaLabel ?? uiAriaLabel;
|
|
368
530
|
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
369
|
-
const [uncontrolledValue, setUncontrolledValue] =
|
|
531
|
+
const [uncontrolledValue, setUncontrolledValue] = useState2(defaultValue);
|
|
370
532
|
const value = controlledValue ?? uncontrolledValue;
|
|
371
533
|
const list = useKeepList({ ...query, tags: value ? [...query?.tags ?? [], value] : query?.tags });
|
|
372
534
|
const select = useCallback(
|
|
@@ -377,16 +539,16 @@ function KeepTagFilter({
|
|
|
377
539
|
},
|
|
378
540
|
[controlledValue, onChange, onValueChange]
|
|
379
541
|
);
|
|
380
|
-
const state =
|
|
542
|
+
const state = useMemo2(
|
|
381
543
|
() => ({ tags: list.tags, tagCounts: list.tagCounts, value, select }),
|
|
382
544
|
[list.tagCounts, list.tags, select, value]
|
|
383
545
|
);
|
|
384
|
-
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */
|
|
385
|
-
/* @__PURE__ */
|
|
386
|
-
/* @__PURE__ */
|
|
387
|
-
list.tags.map((tag) => /* @__PURE__ */
|
|
546
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs2("fieldset", { children: [
|
|
547
|
+
/* @__PURE__ */ jsx4("legend", { children: defaultAriaLabel }),
|
|
548
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-pressed": value === void 0, onClick: () => select(), children: defaultAllLabel }),
|
|
549
|
+
list.tags.map((tag) => /* @__PURE__ */ jsxs2("button", { type: "button", "aria-pressed": value === tag, onClick: () => select(tag), children: [
|
|
388
550
|
renderTag ? renderTag(tag, list.tagCounts[tag] ?? 0, value === tag) : tag,
|
|
389
|
-
/* @__PURE__ */
|
|
551
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
390
552
|
" (",
|
|
391
553
|
list.tagCounts[tag] ?? 0,
|
|
392
554
|
")"
|
|
@@ -418,8 +580,8 @@ function KeepNoteEditor({
|
|
|
418
580
|
const defaultSaveLabel = useUiLabel("saveNote");
|
|
419
581
|
const itemState = useKeepItem(item);
|
|
420
582
|
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
421
|
-
const [note, setNote] =
|
|
422
|
-
|
|
583
|
+
const [note, setNote] = useState2(item.note ?? "");
|
|
584
|
+
useEffect2(() => setNote(itemState.item?.note ?? item.note ?? ""), [item.note, itemState.item?.note]);
|
|
423
585
|
const save = useCallback(async () => {
|
|
424
586
|
const nextNote = note.trim() || void 0;
|
|
425
587
|
try {
|
|
@@ -439,10 +601,10 @@ function KeepNoteEditor({
|
|
|
439
601
|
error: itemState.error,
|
|
440
602
|
save
|
|
441
603
|
};
|
|
442
|
-
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */
|
|
443
|
-
/* @__PURE__ */
|
|
604
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
605
|
+
/* @__PURE__ */ jsxs2("label", { children: [
|
|
444
606
|
label ?? defaultLabel,
|
|
445
|
-
/* @__PURE__ */
|
|
607
|
+
/* @__PURE__ */ jsx4(
|
|
446
608
|
"textarea",
|
|
447
609
|
{
|
|
448
610
|
value: note,
|
|
@@ -452,14 +614,14 @@ function KeepNoteEditor({
|
|
|
452
614
|
}
|
|
453
615
|
)
|
|
454
616
|
] }),
|
|
455
|
-
/* @__PURE__ */
|
|
617
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", disabled: itemState.isMutating, "aria-busy": itemState.isMutating, children: saveLabel ?? defaultSaveLabel })
|
|
456
618
|
] });
|
|
457
619
|
const handleSubmit = (event) => {
|
|
458
620
|
event.preventDefault();
|
|
459
621
|
void save().catch(() => void 0);
|
|
460
622
|
};
|
|
461
623
|
if (!asChild) {
|
|
462
|
-
return /* @__PURE__ */
|
|
624
|
+
return /* @__PURE__ */ jsx4(
|
|
463
625
|
"form",
|
|
464
626
|
{
|
|
465
627
|
...formProps,
|
|
@@ -478,100 +640,6 @@ function KeepNoteEditor({
|
|
|
478
640
|
"KeepNoteEditor"
|
|
479
641
|
);
|
|
480
642
|
}
|
|
481
|
-
function KeepSearchInput({
|
|
482
|
-
value: controlledValue,
|
|
483
|
-
defaultValue = "",
|
|
484
|
-
onValueChange,
|
|
485
|
-
"aria-label": ariaLabel,
|
|
486
|
-
placeholder,
|
|
487
|
-
...props
|
|
488
|
-
}) {
|
|
489
|
-
const label = useUiLabel("search");
|
|
490
|
-
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
491
|
-
const value = controlledValue ?? uncontrolledValue;
|
|
492
|
-
return /* @__PURE__ */ jsx(
|
|
493
|
-
"input",
|
|
494
|
-
{
|
|
495
|
-
...props,
|
|
496
|
-
type: "search",
|
|
497
|
-
value,
|
|
498
|
-
"aria-label": ariaLabel ?? label,
|
|
499
|
-
placeholder: placeholder ?? label,
|
|
500
|
-
onChange: (event) => {
|
|
501
|
-
const nextValue = event.currentTarget.value;
|
|
502
|
-
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
503
|
-
onValueChange?.(nextValue);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
function KeepSortSelect({
|
|
509
|
-
value: controlledValue,
|
|
510
|
-
defaultValue = "updatedAt:desc",
|
|
511
|
-
onValueChange,
|
|
512
|
-
"aria-label": ariaLabel,
|
|
513
|
-
children,
|
|
514
|
-
...props
|
|
515
|
-
}) {
|
|
516
|
-
const label = useUiLabel("sort");
|
|
517
|
-
const newestLabel = useUiLabel("newest");
|
|
518
|
-
const savedLabel = useUiLabel("saved");
|
|
519
|
-
const oldestLabel = useUiLabel("oldest");
|
|
520
|
-
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
521
|
-
const value = controlledValue ?? uncontrolledValue;
|
|
522
|
-
const options = children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
523
|
-
/* @__PURE__ */ jsx("option", { value: "updatedAt:desc", children: newestLabel }),
|
|
524
|
-
/* @__PURE__ */ jsx("option", { value: "savedAt:desc", children: savedLabel }),
|
|
525
|
-
/* @__PURE__ */ jsx("option", { value: "updatedAt:asc", children: oldestLabel }),
|
|
526
|
-
/* @__PURE__ */ jsx("option", { value: "savedAt:asc", children: oldestLabel })
|
|
527
|
-
] });
|
|
528
|
-
return /* @__PURE__ */ jsx(
|
|
529
|
-
"select",
|
|
530
|
-
{
|
|
531
|
-
...props,
|
|
532
|
-
value,
|
|
533
|
-
"aria-label": ariaLabel ?? label,
|
|
534
|
-
onChange: (event) => {
|
|
535
|
-
const nextValue = event.currentTarget.value;
|
|
536
|
-
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
537
|
-
const [by, direction] = nextValue.split(":");
|
|
538
|
-
onValueChange?.(nextValue, { by, direction });
|
|
539
|
-
},
|
|
540
|
-
children: options
|
|
541
|
-
}
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
function KeepPagination({
|
|
545
|
-
totalCount,
|
|
546
|
-
pageSize,
|
|
547
|
-
page = 1,
|
|
548
|
-
onPageChange,
|
|
549
|
-
render,
|
|
550
|
-
...props
|
|
551
|
-
}) {
|
|
552
|
-
const previousPageLabel = useUiLabel("previousPage");
|
|
553
|
-
const nextPageLabel = useUiLabel("nextPage");
|
|
554
|
-
const pageLabel = useUiLabel("page");
|
|
555
|
-
const paginationLabel = useUiLabel("pagination");
|
|
556
|
-
const pageCount = Math.max(1, Math.ceil(totalCount / Math.max(1, pageSize)));
|
|
557
|
-
const currentPage = Math.min(Math.max(1, page), pageCount);
|
|
558
|
-
const goToPage = (nextPage) => {
|
|
559
|
-
const next = Math.min(Math.max(1, nextPage), pageCount);
|
|
560
|
-
onPageChange?.(next, (next - 1) * pageSize);
|
|
561
|
-
};
|
|
562
|
-
if (render) return /* @__PURE__ */ jsx("nav", { ...props, children: render({ page: currentPage, pageCount, goToPage }) });
|
|
563
|
-
return /* @__PURE__ */ jsxs("nav", { ...props, "aria-label": props["aria-label"] ?? paginationLabel, children: [
|
|
564
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => goToPage(currentPage - 1), disabled: currentPage <= 1, children: previousPageLabel }),
|
|
565
|
-
/* @__PURE__ */ jsxs("span", { "aria-current": "page", children: [
|
|
566
|
-
pageLabel,
|
|
567
|
-
" ",
|
|
568
|
-
currentPage,
|
|
569
|
-
" / ",
|
|
570
|
-
pageCount
|
|
571
|
-
] }),
|
|
572
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => goToPage(currentPage + 1), disabled: currentPage >= pageCount, children: nextPageLabel })
|
|
573
|
-
] });
|
|
574
|
-
}
|
|
575
643
|
function KeepTagEditor({
|
|
576
644
|
item,
|
|
577
645
|
availableTags = [],
|
|
@@ -584,9 +652,9 @@ function KeepTagEditor({
|
|
|
584
652
|
const removeLabel = useUiLabel("remove");
|
|
585
653
|
const applyTagsLabel = useUiLabel("applyTags");
|
|
586
654
|
const itemState = useKeepItem(item);
|
|
587
|
-
const [tags, setTags] =
|
|
588
|
-
const [input, setInput] =
|
|
589
|
-
|
|
655
|
+
const [tags, setTags] = useState2(item.tags ?? []);
|
|
656
|
+
const [input, setInput] = useState2("");
|
|
657
|
+
useEffect2(() => setTags(itemState.item?.tags ?? item.tags ?? []), [item.tags, itemState.item?.tags]);
|
|
590
658
|
const save = useCallback(async () => {
|
|
591
659
|
const nextTags = normalizeUiTags(tags);
|
|
592
660
|
try {
|
|
@@ -603,10 +671,10 @@ function KeepTagEditor({
|
|
|
603
671
|
setTags(next);
|
|
604
672
|
setInput("");
|
|
605
673
|
};
|
|
606
|
-
const body = render ? render({ tags, setTags, save, isSaving: itemState.isMutating }) : /* @__PURE__ */
|
|
607
|
-
/* @__PURE__ */
|
|
674
|
+
const body = render ? render({ tags, setTags, save, isSaving: itemState.isMutating }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
675
|
+
/* @__PURE__ */ jsxs2("label", { children: [
|
|
608
676
|
tagsLabel,
|
|
609
|
-
/* @__PURE__ */
|
|
677
|
+
/* @__PURE__ */ jsx4(
|
|
610
678
|
"input",
|
|
611
679
|
{
|
|
612
680
|
value: input,
|
|
@@ -616,19 +684,22 @@ function KeepTagEditor({
|
|
|
616
684
|
if (event.key === "Enter") {
|
|
617
685
|
event.preventDefault();
|
|
618
686
|
if (input.trim()) addTag(input);
|
|
687
|
+
} else if (event.key === "Backspace" && input.length === 0 && tags.length > 0) {
|
|
688
|
+
event.preventDefault();
|
|
689
|
+
setTags(tags.slice(0, -1));
|
|
619
690
|
}
|
|
620
691
|
}
|
|
621
692
|
}
|
|
622
693
|
)
|
|
623
694
|
] }),
|
|
624
|
-
availableTags.length > 0 ? /* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
695
|
+
availableTags.length > 0 ? /* @__PURE__ */ jsx4("datalist", { id: `keep-tags-${item.id}`, children: availableTags.map((tag) => /* @__PURE__ */ jsx4("option", { value: tag }, tag)) }) : null,
|
|
696
|
+
/* @__PURE__ */ jsx4("ul", { "aria-label": tagsLabel, children: tags.map((tag) => /* @__PURE__ */ jsxs2("li", { children: [
|
|
626
697
|
tag,
|
|
627
|
-
/* @__PURE__ */
|
|
698
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: () => setTags(tags.filter((current) => current !== tag)), children: removeLabel })
|
|
628
699
|
] }, tag)) }),
|
|
629
|
-
/* @__PURE__ */
|
|
700
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", disabled: itemState.isMutating, "aria-busy": itemState.isMutating, children: applyTagsLabel })
|
|
630
701
|
] });
|
|
631
|
-
return /* @__PURE__ */
|
|
702
|
+
return /* @__PURE__ */ jsx4(
|
|
632
703
|
"form",
|
|
633
704
|
{
|
|
634
705
|
...props,
|
|
@@ -648,6 +719,8 @@ function KeepBulkActions({
|
|
|
648
719
|
onSelectedIdsChange,
|
|
649
720
|
renderItem,
|
|
650
721
|
onCompleted,
|
|
722
|
+
render,
|
|
723
|
+
children,
|
|
651
724
|
...props
|
|
652
725
|
}) {
|
|
653
726
|
const selectItemsLabel = useUiLabel("selectItems");
|
|
@@ -656,10 +729,10 @@ function KeepBulkActions({
|
|
|
656
729
|
const tagsLabel = useUiLabel("tagsToApply");
|
|
657
730
|
const applyTagsLabel = useUiLabel("applyTags");
|
|
658
731
|
const list = useKeepList(query);
|
|
659
|
-
const [uncontrolledSelectedIds, setUncontrolledSelectedIds] =
|
|
732
|
+
const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState2(defaultSelectedIds);
|
|
660
733
|
const selectedIds = controlledSelectedIds ?? uncontrolledSelectedIds;
|
|
661
734
|
const selected = new Set(selectedIds);
|
|
662
|
-
const [tagsInput, setTagsInput] =
|
|
735
|
+
const [tagsInput, setTagsInput] = useState2("");
|
|
663
736
|
const setSelectedIds = (ids) => {
|
|
664
737
|
if (controlledSelectedIds === void 0) setUncontrolledSelectedIds(ids);
|
|
665
738
|
onSelectedIdsChange?.(ids);
|
|
@@ -678,27 +751,56 @@ function KeepBulkActions({
|
|
|
678
751
|
onCompleted?.("tags", selectedIds);
|
|
679
752
|
setSelectedIds([]);
|
|
680
753
|
};
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
754
|
+
const state = {
|
|
755
|
+
items: list.items,
|
|
756
|
+
selectedIds,
|
|
757
|
+
selectedCount: selectedIds.length,
|
|
758
|
+
allSelected,
|
|
759
|
+
tagsInput,
|
|
760
|
+
setTagsInput,
|
|
761
|
+
toggle,
|
|
762
|
+
toggleAll,
|
|
763
|
+
remove,
|
|
764
|
+
updateTags,
|
|
765
|
+
isMutating: list.isMutating
|
|
766
|
+
};
|
|
767
|
+
const body = render ? render(state) : typeof children === "function" ? children(state) : children ?? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
768
|
+
/* @__PURE__ */ jsxs2("fieldset", { children: [
|
|
769
|
+
/* @__PURE__ */ jsx4("legend", { children: selectItemsLabel }),
|
|
770
|
+
/* @__PURE__ */ jsxs2("label", { children: [
|
|
771
|
+
/* @__PURE__ */ jsx4("input", { type: "checkbox", checked: allSelected, onChange: toggleAll, "aria-label": selectItemsLabel }),
|
|
686
772
|
selectedIds.length,
|
|
687
773
|
" ",
|
|
688
774
|
selectedCountLabel
|
|
689
775
|
] }),
|
|
690
|
-
list.items.map((item) => /* @__PURE__ */
|
|
691
|
-
/* @__PURE__ */
|
|
776
|
+
list.items.map((item) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
777
|
+
/* @__PURE__ */ jsx4(
|
|
778
|
+
KeepItemCheckbox,
|
|
779
|
+
{
|
|
780
|
+
item,
|
|
781
|
+
checked: selected.has(item.id),
|
|
782
|
+
onCheckedChange: () => toggle(item.id)
|
|
783
|
+
}
|
|
784
|
+
),
|
|
692
785
|
renderItem ? renderItem(item, selected.has(item.id)) : getMetaTitle(item.meta) ?? item.id
|
|
693
786
|
] }, item.id))
|
|
694
787
|
] }),
|
|
695
|
-
/* @__PURE__ */
|
|
696
|
-
/* @__PURE__ */
|
|
788
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: () => void remove(), disabled: selectedIds.length === 0 || list.isMutating, children: deleteSelectedLabel }),
|
|
789
|
+
/* @__PURE__ */ jsxs2("label", { children: [
|
|
697
790
|
tagsLabel,
|
|
698
|
-
/* @__PURE__ */
|
|
791
|
+
/* @__PURE__ */ jsx4("input", { value: tagsInput, onChange: (event) => setTagsInput(event.currentTarget.value) })
|
|
699
792
|
] }),
|
|
700
|
-
/* @__PURE__ */
|
|
793
|
+
/* @__PURE__ */ jsx4(
|
|
794
|
+
"button",
|
|
795
|
+
{
|
|
796
|
+
type: "button",
|
|
797
|
+
onClick: () => void updateTags(),
|
|
798
|
+
disabled: selectedIds.length === 0 || list.isMutating,
|
|
799
|
+
children: applyTagsLabel
|
|
800
|
+
}
|
|
801
|
+
)
|
|
701
802
|
] });
|
|
803
|
+
return /* @__PURE__ */ jsx4("section", { ...props, "aria-busy": list.isMutating || props["aria-busy"], children: body });
|
|
702
804
|
}
|
|
703
805
|
function KeepEmptyState({
|
|
704
806
|
title,
|
|
@@ -711,9 +813,9 @@ function KeepEmptyState({
|
|
|
711
813
|
}) {
|
|
712
814
|
const defaultTitle = useUiLabel("noItems").replace(/\.$/, "");
|
|
713
815
|
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
714
|
-
const body = contentChildren ?? /* @__PURE__ */
|
|
715
|
-
/* @__PURE__ */
|
|
716
|
-
description ? /* @__PURE__ */
|
|
816
|
+
const body = contentChildren ?? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
817
|
+
/* @__PURE__ */ jsx4("h2", { children: title ?? defaultTitle }),
|
|
818
|
+
description ? /* @__PURE__ */ jsx4("p", { children: description }) : null,
|
|
717
819
|
action
|
|
718
820
|
] });
|
|
719
821
|
return renderRoot(asChild, children, { ...rootProps, className }, body, "KeepEmptyState");
|
|
@@ -752,9 +854,9 @@ function KeepAnnouncements({ messages, ...props }) {
|
|
|
752
854
|
const savedMessage = useUiLabel("savedMessage", messages?.save);
|
|
753
855
|
const removedMessage = useUiLabel("removedMessage", messages?.remove);
|
|
754
856
|
const noteSavedMessage = useUiLabel("noteSavedMessage", messages?.note);
|
|
755
|
-
const [message, setMessage] =
|
|
857
|
+
const [message, setMessage] = useState2("");
|
|
756
858
|
const lastChangeRef = useRef(void 0);
|
|
757
|
-
|
|
859
|
+
useEffect2(() => {
|
|
758
860
|
const change = context.lastChange;
|
|
759
861
|
if (!change || change === lastChangeRef.current) return;
|
|
760
862
|
lastChangeRef.current = change;
|
|
@@ -762,7 +864,7 @@ function KeepAnnouncements({ messages, ...props }) {
|
|
|
762
864
|
else if (change.action === "remove" || change.action === "removeBatch") setMessage(removedMessage);
|
|
763
865
|
else if (change.action === "updateNote") setMessage(noteSavedMessage);
|
|
764
866
|
}, [context.lastChange, noteSavedMessage, removedMessage, savedMessage]);
|
|
765
|
-
return /* @__PURE__ */
|
|
867
|
+
return /* @__PURE__ */ jsx4("div", { ...props, role: props.role ?? "status", "aria-live": props["aria-live"] ?? "polite", "aria-atomic": "true", children: message });
|
|
766
868
|
}
|
|
767
869
|
function getDerivedStatus(context) {
|
|
768
870
|
if (context.error) return "error";
|
|
@@ -808,7 +910,7 @@ function renderRoot(asChild, child, props, body, componentName) {
|
|
|
808
910
|
if (!isValidElement(child)) throw new Error(`${componentName} with asChild requires a single React element child.`);
|
|
809
911
|
return cloneElement(child, { ...props, children: body });
|
|
810
912
|
}
|
|
811
|
-
return /* @__PURE__ */
|
|
913
|
+
return /* @__PURE__ */ jsx4("div", { ...props, children: body });
|
|
812
914
|
}
|
|
813
915
|
export {
|
|
814
916
|
FallbackStorageAdapter,
|
|
@@ -820,6 +922,7 @@ export {
|
|
|
820
922
|
KeepCollection,
|
|
821
923
|
KeepEmptyState,
|
|
822
924
|
KeepItemCard,
|
|
925
|
+
KeepItemCheckbox,
|
|
823
926
|
KeepKitProvider,
|
|
824
927
|
KeepList,
|
|
825
928
|
KeepNoteEditor,
|