@keepkit/ui 0.17.0 → 0.19.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 +10 -4
- package/dist/index.d.ts +239 -192
- package/dist/index.js +1854 -1374
- package/dist/index.js.map +1 -1
- package/dist/styles/base.css +16 -0
- package/dist/styles/collection.css +163 -0
- package/dist/styles/status.css +76 -0
- package/dist/styles/sync.css +45 -32
- package/dist/theme.css +1 -0
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -6,32 +6,94 @@ import {
|
|
|
6
6
|
createKeepKit as createCoreKeepKit
|
|
7
7
|
} from "@keepkit/core/react";
|
|
8
8
|
|
|
9
|
-
// src/
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
// src/adapters/url-sync.tsx
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_KEEP_URL_PARAMS,
|
|
12
|
+
decodeKeepListQuery,
|
|
13
|
+
encodeKeepListQuery
|
|
14
|
+
} from "@keepkit/core/core";
|
|
15
|
+
import { useEffect, useRef } from "react";
|
|
16
|
+
function createNextPagesRouterAdapter(router) {
|
|
17
|
+
const getUrl = () => router.asPath ?? (typeof window === "undefined" ? "/" : window.location.href);
|
|
18
|
+
return {
|
|
19
|
+
getUrl,
|
|
20
|
+
subscribe: router.events ? (listener) => {
|
|
21
|
+
router.events?.on("routeChangeComplete", listener);
|
|
22
|
+
return () => router.events?.off("routeChangeComplete", listener);
|
|
23
|
+
} : void 0,
|
|
24
|
+
navigate: (url, mode) => {
|
|
25
|
+
void router[mode](url, void 0, { shallow: true });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function useKeepUrlSync({
|
|
30
|
+
enabled = true,
|
|
31
|
+
query,
|
|
32
|
+
onQueryChange,
|
|
33
|
+
options = {},
|
|
34
|
+
adapter: providedAdapter
|
|
35
|
+
}) {
|
|
36
|
+
const browserAdapterRef = useRef(getBrowserAdapter());
|
|
37
|
+
const adapter = providedAdapter ?? browserAdapterRef.current;
|
|
38
|
+
const onQueryChangeRef = useRef(onQueryChange);
|
|
39
|
+
onQueryChangeRef.current = onQueryChange;
|
|
40
|
+
const skipWriteRef = useRef(true);
|
|
41
|
+
const params = options.params;
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!enabled) return;
|
|
44
|
+
const read = () => {
|
|
45
|
+
const url = adapter.getUrl();
|
|
46
|
+
const decoded = decodeKeepListQuery(url, { params });
|
|
47
|
+
skipWriteRef.current = true;
|
|
48
|
+
onQueryChangeRef.current((previousQuery) => ({
|
|
49
|
+
...previousQuery,
|
|
50
|
+
...decoded.search ? { search: decoded.search } : { search: void 0 },
|
|
51
|
+
...decoded.tags ? { tags: decoded.tags } : { tags: void 0 },
|
|
52
|
+
...decoded.sort ? { sort: decoded.sort } : {},
|
|
53
|
+
...decoded.pagination ? { pagination: { ...previousQuery.pagination, ...decoded.pagination } } : {}
|
|
54
|
+
}));
|
|
55
|
+
};
|
|
56
|
+
read();
|
|
57
|
+
return adapter.subscribe?.(read);
|
|
58
|
+
}, [adapter, enabled, params]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!enabled) return;
|
|
61
|
+
if (skipWriteRef.current) {
|
|
62
|
+
skipWriteRef.current = false;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const currentUrl = new URL(adapter.getUrl(), "http://keepkit.invalid");
|
|
66
|
+
const urlParams = { ...DEFAULT_KEEP_URL_PARAMS, ...params };
|
|
67
|
+
for (const key of Object.values(urlParams)) currentUrl.searchParams.delete(key);
|
|
68
|
+
const nextParams = encodeKeepListQuery(query, { params });
|
|
69
|
+
nextParams.forEach((value, key) => {
|
|
70
|
+
currentUrl.searchParams.append(key, value);
|
|
71
|
+
});
|
|
72
|
+
const nextUrl = `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`;
|
|
73
|
+
adapter.navigate(nextUrl, options.history ?? "push");
|
|
74
|
+
}, [adapter, enabled, options.history, params, query]);
|
|
75
|
+
}
|
|
76
|
+
function getBrowserAdapter() {
|
|
77
|
+
return {
|
|
78
|
+
getUrl: () => typeof window === "undefined" ? "/" : window.location.href,
|
|
79
|
+
subscribe: (listener) => {
|
|
80
|
+
if (typeof window === "undefined") return () => void 0;
|
|
81
|
+
window.addEventListener("popstate", listener);
|
|
82
|
+
return () => window.removeEventListener("popstate", listener);
|
|
24
83
|
},
|
|
25
|
-
|
|
26
|
-
|
|
84
|
+
navigate: (url, mode) => {
|
|
85
|
+
if (typeof window === "undefined") return;
|
|
86
|
+
window.history[mode === "push" ? "pushState" : "replaceState"]({}, "", url);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
27
89
|
}
|
|
28
90
|
|
|
29
|
-
// src/hooks/useKeepBackup.ts
|
|
91
|
+
// src/features/actions/hooks/useKeepBackup.ts
|
|
30
92
|
import { useKeepContext } from "@keepkit/core/react";
|
|
31
|
-
import { useRef, useState } from "react";
|
|
93
|
+
import { useRef as useRef2, useState } from "react";
|
|
32
94
|
|
|
33
|
-
// src/ui-context.tsx
|
|
34
|
-
import { createContext, useCallback
|
|
95
|
+
// src/foundation/ui-context.tsx
|
|
96
|
+
import { createContext, useCallback, useContext, useMemo } from "react";
|
|
35
97
|
|
|
36
98
|
// src/locales/de.ts
|
|
37
99
|
var DE_LABELS = {
|
|
@@ -45,6 +107,13 @@ var DE_LABELS = {
|
|
|
45
107
|
allTags: "Alle",
|
|
46
108
|
tags: "Tags",
|
|
47
109
|
filterTags: "Gespeicherte Elemente nach Tag filtern",
|
|
110
|
+
activeFilters: "Aktive Filter",
|
|
111
|
+
clearFilters: "Filter l\xF6schen",
|
|
112
|
+
clearAllFilters: "Alle Filter l\xF6schen",
|
|
113
|
+
removeFilter: "entfernen",
|
|
114
|
+
noFilteredItems: "Keine passenden Elemente gefunden.",
|
|
115
|
+
noFilteredItemsDescription: "Passe die Suche an oder entferne einen Tag-Filter.",
|
|
116
|
+
emptyStorageDescription: "Speichere ein Element, damit es hier angezeigt wird.",
|
|
48
117
|
note: "Notiz",
|
|
49
118
|
saveNote: "Notiz speichern",
|
|
50
119
|
noItems: "Keine gespeicherten Elemente.",
|
|
@@ -124,6 +193,13 @@ var EN_LABELS = {
|
|
|
124
193
|
allTags: "All",
|
|
125
194
|
tags: "Tags",
|
|
126
195
|
filterTags: "Filter saved items by tag",
|
|
196
|
+
activeFilters: "Active filters",
|
|
197
|
+
clearFilters: "Clear filters",
|
|
198
|
+
clearAllFilters: "Clear all filters",
|
|
199
|
+
removeFilter: "Remove",
|
|
200
|
+
noFilteredItems: "No matching items found.",
|
|
201
|
+
noFilteredItemsDescription: "Try adjusting your search or removing a tag filter.",
|
|
202
|
+
emptyStorageDescription: "Save an item to see it here.",
|
|
127
203
|
note: "Note",
|
|
128
204
|
saveNote: "Save note",
|
|
129
205
|
noItems: "No saved items.",
|
|
@@ -203,6 +279,13 @@ var ES_LABELS = {
|
|
|
203
279
|
allTags: "Todos",
|
|
204
280
|
tags: "Etiquetas",
|
|
205
281
|
filterTags: "Filtrar elementos guardados por etiqueta",
|
|
282
|
+
activeFilters: "Filtros activos",
|
|
283
|
+
clearFilters: "Borrar filtros",
|
|
284
|
+
clearAllFilters: "Borrar todos los filtros",
|
|
285
|
+
removeFilter: "quitar",
|
|
286
|
+
noFilteredItems: "No se encontraron elementos coincidentes.",
|
|
287
|
+
noFilteredItemsDescription: "Ajusta la b\xFAsqueda o quita un filtro de etiqueta.",
|
|
288
|
+
emptyStorageDescription: "Guarda un elemento para verlo aqu\xED.",
|
|
206
289
|
note: "Nota",
|
|
207
290
|
saveNote: "Guardar nota",
|
|
208
291
|
noItems: "No hay elementos guardados.",
|
|
@@ -282,6 +365,13 @@ var FIL_LABELS = {
|
|
|
282
365
|
allTags: "Lahat",
|
|
283
366
|
tags: "Mga tag",
|
|
284
367
|
filterTags: "I-filter ang mga naka-save na item ayon sa tag",
|
|
368
|
+
activeFilters: "Mga aktibong filter",
|
|
369
|
+
clearFilters: "I-clear ang mga filter",
|
|
370
|
+
clearAllFilters: "I-clear ang lahat ng filter",
|
|
371
|
+
removeFilter: "alisin",
|
|
372
|
+
noFilteredItems: "Walang nakitang tumutugmang item.",
|
|
373
|
+
noFilteredItemsDescription: "Baguhin ang paghahanap o alisin ang filter ng tag.",
|
|
374
|
+
emptyStorageDescription: "Mag-save ng item para makita ito rito.",
|
|
285
375
|
note: "Tala",
|
|
286
376
|
saveNote: "I-save ang tala",
|
|
287
377
|
noItems: "Walang naka-save na item.",
|
|
@@ -361,6 +451,13 @@ var FR_LABELS = {
|
|
|
361
451
|
allTags: "Tous",
|
|
362
452
|
tags: "\xC9tiquettes",
|
|
363
453
|
filterTags: "Filtrer les \xE9l\xE9ments enregistr\xE9s par \xE9tiquette",
|
|
454
|
+
activeFilters: "Filtres actifs",
|
|
455
|
+
clearFilters: "Effacer les filtres",
|
|
456
|
+
clearAllFilters: "Effacer tous les filtres",
|
|
457
|
+
removeFilter: "supprimer",
|
|
458
|
+
noFilteredItems: "Aucun \xE9l\xE9ment correspondant trouv\xE9.",
|
|
459
|
+
noFilteredItemsDescription: "Modifiez votre recherche ou supprimez un filtre de tag.",
|
|
460
|
+
emptyStorageDescription: "Enregistrez un \xE9l\xE9ment pour le voir ici.",
|
|
364
461
|
note: "Note",
|
|
365
462
|
saveNote: "Enregistrer la note",
|
|
366
463
|
noItems: "Aucun \xE9l\xE9ment enregistr\xE9.",
|
|
@@ -440,6 +537,13 @@ var ID_LABELS = {
|
|
|
440
537
|
allTags: "Semua",
|
|
441
538
|
tags: "Tag",
|
|
442
539
|
filterTags: "Filter item tersimpan berdasarkan tag",
|
|
540
|
+
activeFilters: "Filter aktif",
|
|
541
|
+
clearFilters: "Hapus filter",
|
|
542
|
+
clearAllFilters: "Hapus semua filter",
|
|
543
|
+
removeFilter: "hapus",
|
|
544
|
+
noFilteredItems: "Tidak ada item yang cocok.",
|
|
545
|
+
noFilteredItemsDescription: "Ubah pencarian atau hapus filter tag.",
|
|
546
|
+
emptyStorageDescription: "Simpan item untuk melihatnya di sini.",
|
|
443
547
|
note: "Catatan",
|
|
444
548
|
saveNote: "Simpan catatan",
|
|
445
549
|
noItems: "Belum ada item tersimpan.",
|
|
@@ -519,6 +623,13 @@ var IT_LABELS = {
|
|
|
519
623
|
allTags: "Tutti",
|
|
520
624
|
tags: "Tag",
|
|
521
625
|
filterTags: "Filtra gli elementi salvati per tag",
|
|
626
|
+
activeFilters: "Filtri attivi",
|
|
627
|
+
clearFilters: "Cancella filtri",
|
|
628
|
+
clearAllFilters: "Cancella tutti i filtri",
|
|
629
|
+
removeFilter: "rimuovi",
|
|
630
|
+
noFilteredItems: "Nessun elemento corrispondente trovato.",
|
|
631
|
+
noFilteredItemsDescription: "Modifica la ricerca o rimuovi un filtro tag.",
|
|
632
|
+
emptyStorageDescription: "Salva un elemento per visualizzarlo qui.",
|
|
522
633
|
note: "Nota",
|
|
523
634
|
saveNote: "Salva nota",
|
|
524
635
|
noItems: "Nessun elemento salvato.",
|
|
@@ -598,6 +709,13 @@ var JA_LABELS = {
|
|
|
598
709
|
allTags: "\u3059\u3079\u3066",
|
|
599
710
|
tags: "\u30BF\u30B0",
|
|
600
711
|
filterTags: "\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0\u3092\u30BF\u30B0\u3067\u7D5E\u308A\u8FBC\u3080",
|
|
712
|
+
activeFilters: "\u9069\u7528\u4E2D\u306E\u6761\u4EF6",
|
|
713
|
+
clearFilters: "\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u30AF\u30EA\u30A2",
|
|
714
|
+
clearAllFilters: "\u3059\u3079\u3066\u306E\u6761\u4EF6\u3092\u30AF\u30EA\u30A2",
|
|
715
|
+
removeFilter: "\u3092\u89E3\u9664",
|
|
716
|
+
noFilteredItems: "\u4E00\u81F4\u3059\u308B\u30A2\u30A4\u30C6\u30E0\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F",
|
|
717
|
+
noFilteredItemsDescription: "\u691C\u7D22\u8A9E\u3092\u5909\u66F4\u3059\u308B\u304B\u3001\u30BF\u30B0\u306E\u7D5E\u308A\u8FBC\u307F\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
718
|
+
emptyStorageDescription: "\u30A2\u30A4\u30C6\u30E0\u3092\u4FDD\u5B58\u3059\u308B\u3068\u3001\u3053\u3053\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002",
|
|
601
719
|
note: "\u30E1\u30E2",
|
|
602
720
|
saveNote: "\u30E1\u30E2\u3092\u4FDD\u5B58",
|
|
603
721
|
noItems: "\u4FDD\u5B58\u3055\u308C\u305F\u30A2\u30A4\u30C6\u30E0\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
@@ -677,6 +795,13 @@ var KO_LABELS = {
|
|
|
677
795
|
allTags: "\uBAA8\uB450",
|
|
678
796
|
tags: "\uD0DC\uADF8",
|
|
679
797
|
filterTags: "\uD0DC\uADF8\uB85C \uC800\uC7A5 \uD56D\uBAA9 \uD544\uD130\uB9C1",
|
|
798
|
+
activeFilters: "\uD65C\uC131 \uD544\uD130",
|
|
799
|
+
clearFilters: "\uD544\uD130 \uC9C0\uC6B0\uAE30",
|
|
800
|
+
clearAllFilters: "\uBAA8\uB4E0 \uD544\uD130 \uC9C0\uC6B0\uAE30",
|
|
801
|
+
removeFilter: "\uC81C\uAC70",
|
|
802
|
+
noFilteredItems: "\uC77C\uCE58\uD558\uB294 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
803
|
+
noFilteredItemsDescription: "\uAC80\uC0C9\uC5B4\uB97C \uBC14\uAFB8\uAC70\uB098 \uD0DC\uADF8 \uD544\uD130\uB97C \uC81C\uAC70\uD574 \uBCF4\uC138\uC694.",
|
|
804
|
+
emptyStorageDescription: "\uD56D\uBAA9\uC744 \uC800\uC7A5\uD558\uBA74 \uC5EC\uAE30\uC5D0 \uD45C\uC2DC\uB429\uB2C8\uB2E4.",
|
|
680
805
|
note: "\uBA54\uBAA8",
|
|
681
806
|
saveNote: "\uBA54\uBAA8 \uC800\uC7A5",
|
|
682
807
|
noItems: "\uC800\uC7A5\uB41C \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -756,6 +881,13 @@ var MS_LABELS = {
|
|
|
756
881
|
allTags: "Semua",
|
|
757
882
|
tags: "Tag",
|
|
758
883
|
filterTags: "Tapis item yang disimpan mengikut tag",
|
|
884
|
+
activeFilters: "Penapis aktif",
|
|
885
|
+
clearFilters: "Kosongkan penapis",
|
|
886
|
+
clearAllFilters: "Kosongkan semua penapis",
|
|
887
|
+
removeFilter: "alih keluar",
|
|
888
|
+
noFilteredItems: "Tiada item yang sepadan ditemui.",
|
|
889
|
+
noFilteredItemsDescription: "Laraskan carian atau alih keluar penapis tag.",
|
|
890
|
+
emptyStorageDescription: "Simpan item untuk melihatnya di sini.",
|
|
759
891
|
note: "Nota",
|
|
760
892
|
saveNote: "Simpan nota",
|
|
761
893
|
noItems: "Tiada item disimpan.",
|
|
@@ -835,6 +967,13 @@ var PT_BR_LABELS = {
|
|
|
835
967
|
allTags: "Todos",
|
|
836
968
|
tags: "Tags",
|
|
837
969
|
filterTags: "Filtrar itens salvos por tag",
|
|
970
|
+
activeFilters: "Filtros ativos",
|
|
971
|
+
clearFilters: "Limpar filtros",
|
|
972
|
+
clearAllFilters: "Limpar todos os filtros",
|
|
973
|
+
removeFilter: "remover",
|
|
974
|
+
noFilteredItems: "Nenhum item correspondente encontrado.",
|
|
975
|
+
noFilteredItemsDescription: "Ajuste sua busca ou remova um filtro de tag.",
|
|
976
|
+
emptyStorageDescription: "Salve um item para v\xEA-lo aqui.",
|
|
838
977
|
note: "Nota",
|
|
839
978
|
saveNote: "Salvar nota",
|
|
840
979
|
noItems: "Nenhum item salvo.",
|
|
@@ -914,6 +1053,13 @@ var RU_LABELS = {
|
|
|
914
1053
|
allTags: "\u0412\u0441\u0435",
|
|
915
1054
|
tags: "\u0422\u0435\u0433\u0438",
|
|
916
1055
|
filterTags: "\u0424\u0438\u043B\u044C\u0442\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u043F\u043E \u0442\u0435\u0433\u0443",
|
|
1056
|
+
activeFilters: "\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0444\u0438\u043B\u044C\u0442\u0440\u044B",
|
|
1057
|
+
clearFilters: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0444\u0438\u043B\u044C\u0442\u0440\u044B",
|
|
1058
|
+
clearAllFilters: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0432\u0441\u0435 \u0444\u0438\u043B\u044C\u0442\u0440\u044B",
|
|
1059
|
+
removeFilter: "\u0443\u0434\u0430\u043B\u0438\u0442\u044C",
|
|
1060
|
+
noFilteredItems: "\u041F\u043E\u0434\u0445\u043E\u0434\u044F\u0449\u0438\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u044B.",
|
|
1061
|
+
noFilteredItemsDescription: "\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u0435 \u043F\u043E\u0438\u0441\u043A \u0438\u043B\u0438 \u0443\u0434\u0430\u043B\u0438\u0442\u0435 \u0444\u0438\u043B\u044C\u0442\u0440 \u0442\u0435\u0433\u0430.",
|
|
1062
|
+
emptyStorageDescription: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442, \u0447\u0442\u043E\u0431\u044B \u0443\u0432\u0438\u0434\u0435\u0442\u044C \u0435\u0433\u043E \u0437\u0434\u0435\u0441\u044C.",
|
|
917
1063
|
note: "\u0417\u0430\u043C\u0435\u0442\u043A\u0430",
|
|
918
1064
|
saveNote: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0437\u0430\u043C\u0435\u0442\u043A\u0443",
|
|
919
1065
|
noItems: "\u041D\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0445 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432.",
|
|
@@ -993,6 +1139,13 @@ var TH_LABELS = {
|
|
|
993
1139
|
allTags: "\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
|
|
994
1140
|
tags: "\u0E41\u0E17\u0E47\u0E01",
|
|
995
1141
|
filterTags: "\u0E01\u0E23\u0E2D\u0E07\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E17\u0E35\u0E48\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E14\u0E49\u0E27\u0E22\u0E41\u0E17\u0E47\u0E01",
|
|
1142
|
+
activeFilters: "\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07\u0E17\u0E35\u0E48\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19\u0E2D\u0E22\u0E39\u0E48",
|
|
1143
|
+
clearFilters: "\u0E25\u0E49\u0E32\u0E07\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07",
|
|
1144
|
+
clearAllFilters: "\u0E25\u0E49\u0E32\u0E07\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
|
|
1145
|
+
removeFilter: "\u0E19\u0E33\u0E2D\u0E2D\u0E01",
|
|
1146
|
+
noFilteredItems: "\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E17\u0E35\u0E48\u0E15\u0E23\u0E07\u0E01\u0E31\u0E19",
|
|
1147
|
+
noFilteredItemsDescription: "\u0E25\u0E2D\u0E07\u0E1B\u0E23\u0E31\u0E1A\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E2B\u0E23\u0E37\u0E2D\u0E19\u0E33\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07\u0E41\u0E17\u0E47\u0E01\u0E2D\u0E2D\u0E01",
|
|
1148
|
+
emptyStorageDescription: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E14\u0E39\u0E17\u0E35\u0E48\u0E19\u0E35\u0E48",
|
|
996
1149
|
note: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E22\u0E48\u0E2D",
|
|
997
1150
|
saveNote: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E22\u0E48\u0E2D",
|
|
998
1151
|
noItems: "\u0E44\u0E21\u0E48\u0E21\u0E35\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E17\u0E35\u0E48\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E44\u0E27\u0E49",
|
|
@@ -1072,6 +1225,13 @@ var VI_LABELS = {
|
|
|
1072
1225
|
allTags: "T\u1EA5t c\u1EA3",
|
|
1073
1226
|
tags: "Th\u1EBB",
|
|
1074
1227
|
filterTags: "L\u1ECDc m\u1EE5c \u0111\xE3 l\u01B0u theo th\u1EBB",
|
|
1228
|
+
activeFilters: "B\u1ED9 l\u1ECDc \u0111ang d\xF9ng",
|
|
1229
|
+
clearFilters: "X\xF3a b\u1ED9 l\u1ECDc",
|
|
1230
|
+
clearAllFilters: "X\xF3a t\u1EA5t c\u1EA3 b\u1ED9 l\u1ECDc",
|
|
1231
|
+
removeFilter: "x\xF3a",
|
|
1232
|
+
noFilteredItems: "Kh\xF4ng t\xECm th\u1EA5y m\u1EE5c ph\xF9 h\u1EE3p.",
|
|
1233
|
+
noFilteredItemsDescription: "H\xE3y \u0111i\u1EC1u ch\u1EC9nh t\xECm ki\u1EBFm ho\u1EB7c x\xF3a b\u1ED9 l\u1ECDc th\u1EBB.",
|
|
1234
|
+
emptyStorageDescription: "L\u01B0u m\u1ED9t m\u1EE5c \u0111\u1EC3 xem m\u1EE5c \u0111\xF3 t\u1EA1i \u0111\xE2y.",
|
|
1075
1235
|
note: "Ghi ch\xFA",
|
|
1076
1236
|
saveNote: "L\u01B0u ghi ch\xFA",
|
|
1077
1237
|
noItems: "Ch\u01B0a c\xF3 m\u1EE5c n\xE0o \u0111\u01B0\u1EE3c l\u01B0u.",
|
|
@@ -1151,6 +1311,13 @@ var ZH_HANS_LABELS = {
|
|
|
1151
1311
|
allTags: "\u5168\u90E8",
|
|
1152
1312
|
tags: "\u6807\u7B7E",
|
|
1153
1313
|
filterTags: "\u6309\u6807\u7B7E\u7B5B\u9009\u5DF2\u4FDD\u5B58\u9879\u76EE",
|
|
1314
|
+
activeFilters: "\u5F53\u524D\u7B5B\u9009\u6761\u4EF6",
|
|
1315
|
+
clearFilters: "\u6E05\u9664\u7B5B\u9009",
|
|
1316
|
+
clearAllFilters: "\u6E05\u9664\u6240\u6709\u7B5B\u9009\u6761\u4EF6",
|
|
1317
|
+
removeFilter: "\u79FB\u9664",
|
|
1318
|
+
noFilteredItems: "\u672A\u627E\u5230\u5339\u914D\u9879\u76EE\u3002",
|
|
1319
|
+
noFilteredItemsDescription: "\u8BF7\u8C03\u6574\u641C\u7D22\u6761\u4EF6\u6216\u79FB\u9664\u6807\u7B7E\u7B5B\u9009\u3002",
|
|
1320
|
+
emptyStorageDescription: "\u4FDD\u5B58\u9879\u76EE\u540E\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\u3002",
|
|
1154
1321
|
note: "\u5907\u6CE8",
|
|
1155
1322
|
saveNote: "\u4FDD\u5B58\u5907\u6CE8",
|
|
1156
1323
|
noItems: "\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u9879\u76EE\u3002",
|
|
@@ -1230,6 +1397,13 @@ var ZH_HANT_LABELS = {
|
|
|
1230
1397
|
allTags: "\u5168\u90E8",
|
|
1231
1398
|
tags: "\u6A19\u7C64",
|
|
1232
1399
|
filterTags: "\u4F9D\u6A19\u7C64\u7BE9\u9078\u5DF2\u5132\u5B58\u9805\u76EE",
|
|
1400
|
+
activeFilters: "\u76EE\u524D\u7BE9\u9078\u689D\u4EF6",
|
|
1401
|
+
clearFilters: "\u6E05\u9664\u7BE9\u9078",
|
|
1402
|
+
clearAllFilters: "\u6E05\u9664\u6240\u6709\u7BE9\u9078\u689D\u4EF6",
|
|
1403
|
+
removeFilter: "\u79FB\u9664",
|
|
1404
|
+
noFilteredItems: "\u627E\u4E0D\u5230\u76F8\u7B26\u9805\u76EE\u3002",
|
|
1405
|
+
noFilteredItemsDescription: "\u8ACB\u8ABF\u6574\u641C\u5C0B\u689D\u4EF6\u6216\u79FB\u9664\u6A19\u7C64\u7BE9\u9078\u3002",
|
|
1406
|
+
emptyStorageDescription: "\u5132\u5B58\u9805\u76EE\u5F8C\u6703\u986F\u793A\u5728\u9019\u88E1\u3002",
|
|
1233
1407
|
note: "\u5099\u8A3B",
|
|
1234
1408
|
saveNote: "\u5132\u5B58\u5099\u8A3B",
|
|
1235
1409
|
noItems: "\u6C92\u6709\u5DF2\u5132\u5B58\u7684\u9805\u76EE\u3002",
|
|
@@ -1356,7 +1530,7 @@ function getKeepLocaleLabels(locale) {
|
|
|
1356
1530
|
return { ...KEEP_LOCALE_LABELS[normalizeKeepLocale(locale)] };
|
|
1357
1531
|
}
|
|
1358
1532
|
|
|
1359
|
-
// src/ui-context.tsx
|
|
1533
|
+
// src/foundation/ui-context.tsx
|
|
1360
1534
|
import { jsx } from "react/jsx-runtime";
|
|
1361
1535
|
var DEFAULT_LABELS = KEEP_LOCALE_LABELS.en;
|
|
1362
1536
|
var KeepUiLabelsContext = createContext({
|
|
@@ -1370,7 +1544,7 @@ function KeepUiProvider({
|
|
|
1370
1544
|
onFeedback,
|
|
1371
1545
|
children
|
|
1372
1546
|
}) {
|
|
1373
|
-
const emitFeedback =
|
|
1547
|
+
const emitFeedback = useCallback(
|
|
1374
1548
|
(event) => {
|
|
1375
1549
|
onFeedback?.(event);
|
|
1376
1550
|
},
|
|
@@ -1393,13 +1567,13 @@ function useUiLabel(key, override) {
|
|
|
1393
1567
|
}
|
|
1394
1568
|
function useKeepUiFeedback() {
|
|
1395
1569
|
const { emitFeedback } = useKeepUiLabels();
|
|
1396
|
-
return
|
|
1570
|
+
return useCallback((event) => emitFeedback(event), [emitFeedback]);
|
|
1397
1571
|
}
|
|
1398
1572
|
|
|
1399
|
-
// src/hooks/useKeepBackup.ts
|
|
1573
|
+
// src/features/actions/hooks/useKeepBackup.ts
|
|
1400
1574
|
function useKeepBackup({ filename, onExport, onImported }) {
|
|
1401
1575
|
const context = useKeepContext();
|
|
1402
|
-
const inputRef =
|
|
1576
|
+
const inputRef = useRef2(null);
|
|
1403
1577
|
const [mode, setMode] = useState("merge");
|
|
1404
1578
|
const [result, setResult] = useState();
|
|
1405
1579
|
const [error, setError] = useState();
|
|
@@ -1456,7 +1630,7 @@ function useKeepBackup({ filename, onExport, onImported }) {
|
|
|
1456
1630
|
};
|
|
1457
1631
|
}
|
|
1458
1632
|
|
|
1459
|
-
// src/KeepBackup.tsx
|
|
1633
|
+
// src/features/actions/KeepBackup.tsx
|
|
1460
1634
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1461
1635
|
function KeepBackup({
|
|
1462
1636
|
filename = "keepkit-backup.json",
|
|
@@ -1533,11 +1707,7 @@ function getErrorMessage(error) {
|
|
|
1533
1707
|
return error instanceof Error ? error.message : "Something went wrong.";
|
|
1534
1708
|
}
|
|
1535
1709
|
|
|
1536
|
-
// src/
|
|
1537
|
-
import { useKeepList } from "@keepkit/core/react";
|
|
1538
|
-
import { useState as useState2 } from "react";
|
|
1539
|
-
|
|
1540
|
-
// src/shared.tsx
|
|
1710
|
+
// src/foundation/shared.tsx
|
|
1541
1711
|
import {
|
|
1542
1712
|
cloneElement,
|
|
1543
1713
|
createContext as createContext2,
|
|
@@ -1606,15 +1776,69 @@ function sortToValue(sort) {
|
|
|
1606
1776
|
function resolveContent(content, state) {
|
|
1607
1777
|
return typeof content === "function" ? content(state) : content;
|
|
1608
1778
|
}
|
|
1779
|
+
function chainedFunction(childHandler, parentHandler) {
|
|
1780
|
+
if (!childHandler) return parentHandler;
|
|
1781
|
+
if (!parentHandler) return childHandler;
|
|
1782
|
+
return ((...args) => {
|
|
1783
|
+
childHandler(...args);
|
|
1784
|
+
parentHandler(...args);
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
function mergeProps(childProps, parentProps) {
|
|
1788
|
+
const merged = { ...parentProps, ...childProps };
|
|
1789
|
+
const className = mergeClassNames(childProps.className, parentProps.className);
|
|
1790
|
+
if (className) merged.className = className;
|
|
1791
|
+
const style = mergeStyles(childProps.style, parentProps.style);
|
|
1792
|
+
if (style) merged.style = style;
|
|
1793
|
+
for (const key of Object.keys(parentProps)) {
|
|
1794
|
+
if (!isEventProp(key)) continue;
|
|
1795
|
+
const chained = chainedFunction(getHandler(childProps[key]), getHandler(parentProps[key]));
|
|
1796
|
+
if (chained) merged[key] = chained;
|
|
1797
|
+
}
|
|
1798
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
1799
|
+
if (value === void 0 && key.startsWith("aria-") && childProps[key] !== void 0) {
|
|
1800
|
+
merged[key] = childProps[key];
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return merged;
|
|
1804
|
+
}
|
|
1805
|
+
function getHandler(value) {
|
|
1806
|
+
return typeof value === "function" ? value : void 0;
|
|
1807
|
+
}
|
|
1808
|
+
function isEventProp(key) {
|
|
1809
|
+
return key.startsWith("on") && key.length > 2 && key[2] === key[2]?.toUpperCase();
|
|
1810
|
+
}
|
|
1811
|
+
function mergeClassNames(childClassName, parentClassName) {
|
|
1812
|
+
const values = [childClassName, parentClassName].filter(
|
|
1813
|
+
(value) => typeof value === "string" && Boolean(value)
|
|
1814
|
+
);
|
|
1815
|
+
return values.length > 0 ? values.join(" ") : void 0;
|
|
1816
|
+
}
|
|
1817
|
+
function mergeStyles(childStyle, parentStyle) {
|
|
1818
|
+
if (!childStyle && !parentStyle) return void 0;
|
|
1819
|
+
return {
|
|
1820
|
+
...isObject(childStyle) ? childStyle : {},
|
|
1821
|
+
...isObject(parentStyle) ? parentStyle : {}
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
function isObject(value) {
|
|
1825
|
+
return typeof value === "object" && value !== null;
|
|
1826
|
+
}
|
|
1827
|
+
function createSlot(child, props, body) {
|
|
1828
|
+
const nextProps = body === void 0 ? props : { ...props, children: body };
|
|
1829
|
+
return cloneElement(child, mergeProps(child.props, nextProps));
|
|
1830
|
+
}
|
|
1609
1831
|
function renderRoot(asChild, child, props, body, componentName) {
|
|
1610
1832
|
if (asChild) {
|
|
1611
1833
|
if (!isValidElement(child)) throw new Error(`${componentName} with asChild requires a single React element child.`);
|
|
1612
|
-
return
|
|
1834
|
+
return createSlot(child, props, body);
|
|
1613
1835
|
}
|
|
1614
1836
|
return /* @__PURE__ */ jsx3("div", { ...props, children: body });
|
|
1615
1837
|
}
|
|
1616
1838
|
|
|
1617
|
-
// src/hooks/useKeepBulkActions.ts
|
|
1839
|
+
// src/features/actions/hooks/useKeepBulkActions.ts
|
|
1840
|
+
import { useKeepList } from "@keepkit/core/react";
|
|
1841
|
+
import { useState as useState2 } from "react";
|
|
1618
1842
|
function isAllSelected(items, selectedIds) {
|
|
1619
1843
|
if (items.length === 0) return false;
|
|
1620
1844
|
const selected = new Set(selectedIds);
|
|
@@ -1707,7 +1931,7 @@ function useKeepBulkActions(options) {
|
|
|
1707
1931
|
};
|
|
1708
1932
|
}
|
|
1709
1933
|
|
|
1710
|
-
// src/KeepItemCheckbox.tsx
|
|
1934
|
+
// src/features/actions/KeepItemCheckbox.tsx
|
|
1711
1935
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1712
1936
|
function KeepItemCheckbox({
|
|
1713
1937
|
item,
|
|
@@ -1740,7 +1964,7 @@ function getItemLabel(item) {
|
|
|
1740
1964
|
return typeof title === "string" && title.trim() ? title.trim() : void 0;
|
|
1741
1965
|
}
|
|
1742
1966
|
|
|
1743
|
-
// src/KeepBulkActions.tsx
|
|
1967
|
+
// src/features/actions/KeepBulkActions.tsx
|
|
1744
1968
|
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1745
1969
|
function KeepBulkActions({
|
|
1746
1970
|
query,
|
|
@@ -1855,13 +2079,13 @@ function KeepBulkActions({
|
|
|
1855
2079
|
);
|
|
1856
2080
|
}
|
|
1857
2081
|
|
|
1858
|
-
// src/KeepButton.tsx
|
|
2082
|
+
// src/features/actions/KeepButton.tsx
|
|
1859
2083
|
import {
|
|
1860
2084
|
KeepButton as CoreKeepButton
|
|
1861
2085
|
} from "@keepkit/core/react";
|
|
1862
|
-
import { createElement, useEffect, useRef as
|
|
2086
|
+
import { createElement, useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
1863
2087
|
|
|
1864
|
-
// src/hooks/useKeepButton.ts
|
|
2088
|
+
// src/features/actions/hooks/useKeepButton.ts
|
|
1865
2089
|
import { useKeepItem } from "@keepkit/core/react";
|
|
1866
2090
|
function useKeepButton({ item, labels, icons, children }) {
|
|
1867
2091
|
return {
|
|
@@ -1881,7 +2105,7 @@ function useKeepButton({ item, labels, icons, children }) {
|
|
|
1881
2105
|
};
|
|
1882
2106
|
}
|
|
1883
2107
|
|
|
1884
|
-
// src/KeepButton.tsx
|
|
2108
|
+
// src/features/actions/KeepButton.tsx
|
|
1885
2109
|
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1886
2110
|
function KeepButton({
|
|
1887
2111
|
labels,
|
|
@@ -1894,8 +2118,8 @@ function KeepButton({
|
|
|
1894
2118
|
...props
|
|
1895
2119
|
}) {
|
|
1896
2120
|
const view = useKeepButton({ item: props.item, labels, icons, children: props.children });
|
|
1897
|
-
const pendingToggle =
|
|
1898
|
-
|
|
2121
|
+
const pendingToggle = useRef3(null);
|
|
2122
|
+
useEffect2(() => {
|
|
1899
2123
|
const pending = pendingToggle.current;
|
|
1900
2124
|
if (!pending || view.buttonState.isMutating || pending.wasSaved === view.buttonState.isSaved) return;
|
|
1901
2125
|
pendingToggle.current = null;
|
|
@@ -1962,148 +2186,495 @@ function renderIcon(icon, className) {
|
|
|
1962
2186
|
return icon ?? null;
|
|
1963
2187
|
}
|
|
1964
2188
|
|
|
1965
|
-
// src/
|
|
1966
|
-
import {
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
router.events?.on("routeChangeComplete", listener);
|
|
1985
|
-
return () => router.events?.off("routeChangeComplete", listener);
|
|
1986
|
-
} : void 0,
|
|
1987
|
-
navigate: (url, mode) => {
|
|
1988
|
-
void router[mode](url, void 0, { shallow: true });
|
|
1989
|
-
}
|
|
1990
|
-
};
|
|
1991
|
-
}
|
|
1992
|
-
function useKeepUrlSync({
|
|
1993
|
-
enabled = true,
|
|
1994
|
-
query,
|
|
1995
|
-
onQueryChange,
|
|
1996
|
-
options = {},
|
|
1997
|
-
adapter: providedAdapter
|
|
1998
|
-
}) {
|
|
1999
|
-
const browserAdapterRef = useRef3(getBrowserAdapter());
|
|
2000
|
-
const adapter = providedAdapter ?? browserAdapterRef.current;
|
|
2001
|
-
const onQueryChangeRef = useRef3(onQueryChange);
|
|
2002
|
-
onQueryChangeRef.current = onQueryChange;
|
|
2003
|
-
const skipWriteRef = useRef3(true);
|
|
2004
|
-
const params = options.params;
|
|
2005
|
-
useEffect2(() => {
|
|
2006
|
-
if (!enabled) return;
|
|
2007
|
-
const read = () => {
|
|
2008
|
-
const url = adapter.getUrl();
|
|
2009
|
-
const decoded = decodeKeepListQuery(url, { params });
|
|
2010
|
-
skipWriteRef.current = true;
|
|
2011
|
-
onQueryChangeRef.current((previousQuery) => ({
|
|
2012
|
-
...previousQuery,
|
|
2013
|
-
...decoded.search ? { search: decoded.search } : { search: void 0 },
|
|
2014
|
-
...decoded.tags ? { tags: decoded.tags } : { tags: void 0 },
|
|
2015
|
-
...decoded.sort ? { sort: decoded.sort } : {},
|
|
2016
|
-
...decoded.pagination ? { pagination: { ...previousQuery.pagination, ...decoded.pagination } } : {}
|
|
2017
|
-
}));
|
|
2018
|
-
};
|
|
2019
|
-
read();
|
|
2020
|
-
return adapter.subscribe?.(read);
|
|
2021
|
-
}, [adapter, enabled, params]);
|
|
2022
|
-
useEffect2(() => {
|
|
2023
|
-
if (!enabled) return;
|
|
2024
|
-
if (skipWriteRef.current) {
|
|
2025
|
-
skipWriteRef.current = false;
|
|
2026
|
-
return;
|
|
2027
|
-
}
|
|
2028
|
-
const currentUrl = new URL(adapter.getUrl(), "http://keepkit.invalid");
|
|
2029
|
-
const urlParams = { ...DEFAULT_KEEP_URL_PARAMS, ...params };
|
|
2030
|
-
for (const key of Object.values(urlParams)) currentUrl.searchParams.delete(key);
|
|
2031
|
-
const nextParams = encodeKeepListQuery(query, { params });
|
|
2032
|
-
nextParams.forEach((value, key) => {
|
|
2033
|
-
currentUrl.searchParams.append(key, value);
|
|
2034
|
-
});
|
|
2035
|
-
const nextUrl = `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`;
|
|
2036
|
-
adapter.navigate(nextUrl, options.history ?? "push");
|
|
2037
|
-
}, [adapter, enabled, options.history, params, query]);
|
|
2038
|
-
}
|
|
2039
|
-
function getBrowserAdapter() {
|
|
2189
|
+
// src/features/actions/hooks/useKeepUndo.ts
|
|
2190
|
+
import { useKeepContext as useKeepContext2 } from "@keepkit/core/react";
|
|
2191
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
2192
|
+
function useKeepUndo() {
|
|
2193
|
+
const context = useKeepContext2();
|
|
2194
|
+
const emitFeedback = useKeepUiFeedback();
|
|
2195
|
+
const restoredMessage = useUiLabel("restoredMessage");
|
|
2196
|
+
const { canUndo, startedAt, expiresAt } = context.undo;
|
|
2197
|
+
const [now, setNow] = useState3(() => Date.now());
|
|
2198
|
+
useEffect3(() => {
|
|
2199
|
+
if (!canUndo || expiresAt === void 0) return;
|
|
2200
|
+
setNow(Date.now());
|
|
2201
|
+
const timer = setInterval(() => setNow(Date.now()), 250);
|
|
2202
|
+
return () => clearInterval(timer);
|
|
2203
|
+
}, [canUndo, expiresAt]);
|
|
2204
|
+
const duration = Math.max(1, (expiresAt ?? now) - (startedAt ?? now));
|
|
2205
|
+
const remainingMs = Math.max(0, (expiresAt ?? now) - now);
|
|
2206
|
+
const remainingSeconds = Math.max(0, Math.ceil(remainingMs / 1e3));
|
|
2207
|
+
const progress = Math.min(1, Math.max(0, remainingMs / duration));
|
|
2040
2208
|
return {
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2209
|
+
canUndo,
|
|
2210
|
+
expiresAt,
|
|
2211
|
+
remainingMs,
|
|
2212
|
+
remainingSeconds,
|
|
2213
|
+
progress,
|
|
2214
|
+
undo: async () => {
|
|
2215
|
+
const items = context.lastChange?.items ?? (context.lastChange?.item ? [context.lastChange.item] : []);
|
|
2216
|
+
await context.undoLastRemoval();
|
|
2217
|
+
if (items.length > 0) {
|
|
2218
|
+
emitFeedback({ type: "item-restored", item: items[0], items, message: restoredMessage });
|
|
2219
|
+
}
|
|
2046
2220
|
},
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
window.history[mode === "push" ? "pushState" : "replaceState"]({}, "", url);
|
|
2050
|
-
}
|
|
2221
|
+
message: useUiLabel("undoAvailable"),
|
|
2222
|
+
label: useUiLabel("undo")
|
|
2051
2223
|
};
|
|
2052
2224
|
}
|
|
2053
2225
|
|
|
2054
|
-
// src/
|
|
2055
|
-
|
|
2226
|
+
// src/features/actions/KeepUndo.tsx
|
|
2227
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2228
|
+
function KeepUndo({ children, label, ...props }) {
|
|
2229
|
+
const view = useKeepUndo();
|
|
2230
|
+
if (!view.canUndo) return null;
|
|
2231
|
+
return /* @__PURE__ */ jsxs4("div", { ...props, role: "status", "aria-live": "polite", "data-keepkit": "undo", "data-state": "available", children: [
|
|
2232
|
+
/* @__PURE__ */ jsx7("span", { "data-keepkit": "undo-message", children: children ?? view.message }),
|
|
2233
|
+
/* @__PURE__ */ jsxs4("span", { "data-keepkit": "undo-countdown", "aria-hidden": "true", children: [
|
|
2234
|
+
view.remainingSeconds,
|
|
2235
|
+
"s"
|
|
2236
|
+
] }),
|
|
2237
|
+
/* @__PURE__ */ jsx7(
|
|
2238
|
+
"progress",
|
|
2239
|
+
{
|
|
2240
|
+
"data-keepkit": "undo-progress",
|
|
2241
|
+
max: 1,
|
|
2242
|
+
value: view.progress,
|
|
2243
|
+
"aria-label": String(view.label),
|
|
2244
|
+
"aria-valuetext": `${view.remainingSeconds}s`
|
|
2245
|
+
}
|
|
2246
|
+
),
|
|
2247
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "data-keep-action": "undo", onClick: () => void view.undo(), children: label ?? view.label })
|
|
2248
|
+
] });
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// src/features/collection/KeepCollection.tsx
|
|
2252
|
+
import { KeepErrorBoundary as KeepErrorBoundary2 } from "@keepkit/core/react";
|
|
2253
|
+
|
|
2254
|
+
// src/features/query/KeepActiveFiltersSummary.tsx
|
|
2255
|
+
import { isValidElement as isValidElement2 } from "react";
|
|
2256
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2257
|
+
function KeepActiveFiltersSummary({
|
|
2056
2258
|
query,
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2259
|
+
search: providedSearch,
|
|
2260
|
+
tags: providedTags,
|
|
2261
|
+
onSearchChange,
|
|
2262
|
+
onTagChange,
|
|
2263
|
+
onClear,
|
|
2264
|
+
children,
|
|
2265
|
+
asChild = false,
|
|
2266
|
+
className,
|
|
2267
|
+
...rootProps
|
|
2061
2268
|
}) {
|
|
2062
|
-
const
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2269
|
+
const search = (providedSearch ?? query?.search?.query ?? "").trim();
|
|
2270
|
+
const tags = normalizeTags(providedTags ?? query?.tags ?? []);
|
|
2271
|
+
const hasFilters = Boolean(search) || tags.length > 0;
|
|
2272
|
+
const activeFiltersLabel = useUiLabel("activeFilters");
|
|
2273
|
+
const clearAllLabel = useUiLabel("clearAllFilters");
|
|
2274
|
+
const clearLabel = useUiLabel("clearFilters");
|
|
2275
|
+
const removeLabel = useUiLabel("removeFilter");
|
|
2276
|
+
const state = {
|
|
2277
|
+
search,
|
|
2278
|
+
tags,
|
|
2279
|
+
hasFilters,
|
|
2280
|
+
clear: () => onClear?.(),
|
|
2281
|
+
removeSearch: () => onSearchChange?.(""),
|
|
2282
|
+
removeTag: (tag) => onTagChange?.(tag)
|
|
2069
2283
|
};
|
|
2070
|
-
const
|
|
2071
|
-
const
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2284
|
+
const contentChildren = asChild && isElement(children) ? void 0 : children;
|
|
2285
|
+
const body = typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? (hasFilters ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2286
|
+
/* @__PURE__ */ jsx8("span", { "data-active-filters-label": "true", children: activeFiltersLabel }),
|
|
2287
|
+
/* @__PURE__ */ jsxs5("ul", { "data-active-filters-list": "true", children: [
|
|
2288
|
+
search ? /* @__PURE__ */ jsxs5("li", { "data-filter-kind": "search", children: [
|
|
2289
|
+
/* @__PURE__ */ jsx8("span", { "data-filter-value": "true", children: search }),
|
|
2290
|
+
/* @__PURE__ */ jsx8(
|
|
2291
|
+
"button",
|
|
2292
|
+
{
|
|
2293
|
+
type: "button",
|
|
2294
|
+
"data-keep-action": "remove-search-filter",
|
|
2295
|
+
"aria-label": `${search} ${removeLabel}`,
|
|
2296
|
+
onClick: state.removeSearch,
|
|
2297
|
+
children: "\xD7"
|
|
2298
|
+
}
|
|
2299
|
+
)
|
|
2300
|
+
] }) : null,
|
|
2301
|
+
tags.map((tag) => /* @__PURE__ */ jsxs5("li", { "data-filter-kind": "tag", children: [
|
|
2302
|
+
/* @__PURE__ */ jsx8("span", { "data-filter-value": "true", children: tag }),
|
|
2303
|
+
/* @__PURE__ */ jsx8(
|
|
2304
|
+
"button",
|
|
2305
|
+
{
|
|
2306
|
+
type: "button",
|
|
2307
|
+
"data-keep-action": "remove-tag-filter",
|
|
2308
|
+
"aria-label": `${tag} ${removeLabel}`,
|
|
2309
|
+
onClick: () => state.removeTag(tag),
|
|
2310
|
+
children: "\xD7"
|
|
2311
|
+
}
|
|
2312
|
+
)
|
|
2313
|
+
] }, tag))
|
|
2314
|
+
] }),
|
|
2315
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "data-keep-action": "clear-filters", onClick: state.clear, children: clearAllLabel || clearLabel })
|
|
2316
|
+
] }) : null);
|
|
2317
|
+
if (!hasFilters && !asChild && contentChildren === void 0) return null;
|
|
2318
|
+
return renderRoot(
|
|
2319
|
+
asChild,
|
|
2320
|
+
isElement(children) ? children : void 0,
|
|
2321
|
+
{
|
|
2322
|
+
...rootProps,
|
|
2323
|
+
className,
|
|
2324
|
+
"data-keepkit": "active-filters",
|
|
2325
|
+
"data-state": hasFilters ? "active" : "idle",
|
|
2326
|
+
"aria-label": rootProps["aria-label"] ?? activeFiltersLabel
|
|
2327
|
+
},
|
|
2328
|
+
body,
|
|
2329
|
+
"KeepActiveFiltersSummary"
|
|
2084
2330
|
);
|
|
2085
|
-
|
|
2331
|
+
}
|
|
2332
|
+
function normalizeTags(tags) {
|
|
2333
|
+
return [...new Set(tags.map((tag) => tag.trim()).filter(Boolean))];
|
|
2334
|
+
}
|
|
2335
|
+
function isElement(value) {
|
|
2336
|
+
return isValidElement2(value);
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// src/features/query/KeepTagFilter.tsx
|
|
2340
|
+
import { isValidElement as isValidElement3 } from "react";
|
|
2341
|
+
|
|
2342
|
+
// src/features/query/hooks/useKeepTagFilter.ts
|
|
2343
|
+
import { useKeepList as useKeepList2 } from "@keepkit/core/react";
|
|
2344
|
+
import { useCallback as useCallback2, useMemo as useMemo2, useState as useState4 } from "react";
|
|
2345
|
+
function useKeepTagFilter(options) {
|
|
2346
|
+
const { query, controlledValue, defaultValue, onChange, onValueChange } = options;
|
|
2347
|
+
const [uncontrolledValue, setUncontrolledValue] = useState4(defaultValue);
|
|
2348
|
+
const resolvedValue = controlledValue ?? uncontrolledValue;
|
|
2349
|
+
const list = useKeepList2({
|
|
2350
|
+
...query,
|
|
2351
|
+
tags: resolvedValue ? [...query?.tags ?? [], resolvedValue] : query?.tags
|
|
2352
|
+
});
|
|
2353
|
+
const select = useCallback2(
|
|
2354
|
+
(tag) => {
|
|
2355
|
+
if (controlledValue === void 0) setUncontrolledValue(tag);
|
|
2356
|
+
onChange?.(tag);
|
|
2357
|
+
onValueChange?.(tag);
|
|
2358
|
+
},
|
|
2359
|
+
[controlledValue, onChange, onValueChange]
|
|
2360
|
+
);
|
|
2361
|
+
const state = useMemo2(
|
|
2362
|
+
() => ({ tags: list.tags, tagCounts: list.tagCounts, value: resolvedValue, select }),
|
|
2363
|
+
[list.tagCounts, list.tags, resolvedValue, select]
|
|
2364
|
+
);
|
|
2365
|
+
return {
|
|
2366
|
+
state,
|
|
2367
|
+
isLoading: list.isLoading,
|
|
2368
|
+
labels: { all: useUiLabel("allTags"), aria: useUiLabel("filterTags") }
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
// src/features/query/KeepTagFilter.tsx
|
|
2373
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2374
|
+
function KeepTagFilter({
|
|
2375
|
+
query,
|
|
2376
|
+
value: controlledValue,
|
|
2377
|
+
defaultValue,
|
|
2378
|
+
onChange,
|
|
2379
|
+
onValueChange,
|
|
2380
|
+
allLabel,
|
|
2381
|
+
ariaLabel,
|
|
2382
|
+
renderTag,
|
|
2383
|
+
render,
|
|
2384
|
+
children,
|
|
2385
|
+
asChild = false,
|
|
2386
|
+
className,
|
|
2387
|
+
...rootProps
|
|
2388
|
+
}) {
|
|
2389
|
+
const view = useKeepTagFilter({ query, controlledValue, defaultValue, onChange, onValueChange });
|
|
2390
|
+
const contentChildren = asChild && isValidElement3(children) ? void 0 : children;
|
|
2391
|
+
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? /* @__PURE__ */ jsxs6("fieldset", { children: [
|
|
2392
|
+
/* @__PURE__ */ jsx9("legend", { children: ariaLabel ?? view.labels.aria }),
|
|
2393
|
+
/* @__PURE__ */ jsx9(
|
|
2394
|
+
"button",
|
|
2395
|
+
{
|
|
2396
|
+
type: "button",
|
|
2397
|
+
"data-keep-action": "filter-all-tags",
|
|
2398
|
+
"aria-pressed": view.state.value === void 0,
|
|
2399
|
+
onClick: () => view.state.select(),
|
|
2400
|
+
children: allLabel ?? view.labels.all
|
|
2401
|
+
}
|
|
2402
|
+
),
|
|
2403
|
+
view.state.tags.map((tag) => /* @__PURE__ */ jsxs6(
|
|
2404
|
+
"button",
|
|
2405
|
+
{
|
|
2406
|
+
type: "button",
|
|
2407
|
+
"data-keep-action": "filter-tag",
|
|
2408
|
+
"aria-pressed": view.state.value === tag,
|
|
2409
|
+
onClick: () => view.state.select(tag),
|
|
2410
|
+
children: [
|
|
2411
|
+
renderTag ? renderTag(tag, view.state.tagCounts[tag] ?? 0, view.state.value === tag) : tag,
|
|
2412
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
2413
|
+
" (",
|
|
2414
|
+
view.state.tagCounts[tag] ?? 0,
|
|
2415
|
+
")"
|
|
2416
|
+
] })
|
|
2417
|
+
]
|
|
2418
|
+
},
|
|
2419
|
+
tag
|
|
2420
|
+
))
|
|
2421
|
+
] });
|
|
2422
|
+
return renderRoot(
|
|
2423
|
+
asChild,
|
|
2424
|
+
isValidElement3(children) ? children : void 0,
|
|
2425
|
+
{
|
|
2426
|
+
...rootProps,
|
|
2427
|
+
className,
|
|
2428
|
+
"data-keepkit": "tag-filter",
|
|
2429
|
+
"data-state": view.state.value === void 0 ? "all" : "filtered",
|
|
2430
|
+
"data-loading": view.isLoading ? "true" : void 0
|
|
2431
|
+
},
|
|
2432
|
+
body,
|
|
2433
|
+
"KeepTagFilter"
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// src/features/query/hooks/useQueryControls.ts
|
|
2438
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
2439
|
+
function useKeepSearchInput(options) {
|
|
2440
|
+
const { controlledValue, defaultValue, debounceMs, onValueChange } = options;
|
|
2441
|
+
const [uncontrolledValue, setUncontrolledValue] = useState5(defaultValue);
|
|
2442
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
2443
|
+
useEffect4(() => {
|
|
2444
|
+
if (!onValueChange) return;
|
|
2445
|
+
if (debounceMs <= 0) {
|
|
2446
|
+
onValueChange(value);
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
const timer = window.setTimeout(() => onValueChange(value), debounceMs);
|
|
2450
|
+
return () => window.clearTimeout(timer);
|
|
2451
|
+
}, [debounceMs, onValueChange, value]);
|
|
2452
|
+
return {
|
|
2453
|
+
value,
|
|
2454
|
+
label: useUiLabel("search"),
|
|
2455
|
+
change: (event) => {
|
|
2456
|
+
if (controlledValue === void 0) setUncontrolledValue(event.currentTarget.value);
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
}
|
|
2460
|
+
function useKeepSortSelect(options) {
|
|
2461
|
+
const { controlledValue, defaultValue, onValueChange } = options;
|
|
2462
|
+
const [uncontrolledValue, setUncontrolledValue] = useState5(defaultValue);
|
|
2463
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
2464
|
+
return {
|
|
2465
|
+
value,
|
|
2466
|
+
change: (event) => {
|
|
2467
|
+
const nextValue = event.currentTarget.value;
|
|
2468
|
+
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
2469
|
+
const [by, direction] = nextValue.split(":");
|
|
2470
|
+
onValueChange?.(nextValue, { by, direction });
|
|
2471
|
+
},
|
|
2472
|
+
labels: {
|
|
2473
|
+
sort: useUiLabel("sort"),
|
|
2474
|
+
updatedNewest: useUiLabel("updatedNewest"),
|
|
2475
|
+
updatedOldest: useUiLabel("updatedOldest"),
|
|
2476
|
+
savedNewest: useUiLabel("savedNewest"),
|
|
2477
|
+
savedOldest: useUiLabel("savedOldest")
|
|
2478
|
+
}
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
function useKeepPagination(options) {
|
|
2482
|
+
const { totalCount, pageSize, page, maxPageButtons, onPageChange } = options;
|
|
2483
|
+
const pageCount = Math.max(1, Math.ceil(totalCount / Math.max(1, pageSize)));
|
|
2484
|
+
const currentPage = Math.min(Math.max(1, page), pageCount);
|
|
2485
|
+
const goToPage = (nextPage) => {
|
|
2486
|
+
const next = Math.min(Math.max(1, nextPage), pageCount);
|
|
2487
|
+
onPageChange?.(next, (next - 1) * pageSize);
|
|
2488
|
+
};
|
|
2489
|
+
return {
|
|
2490
|
+
pageCount,
|
|
2491
|
+
currentPage,
|
|
2492
|
+
goToPage,
|
|
2493
|
+
visiblePages: getVisiblePages(currentPage, pageCount, Math.max(1, maxPageButtons)),
|
|
2494
|
+
labels: {
|
|
2495
|
+
previous: useUiLabel("previousPage"),
|
|
2496
|
+
next: useUiLabel("nextPage"),
|
|
2497
|
+
page: useUiLabel("page"),
|
|
2498
|
+
pagination: useUiLabel("pagination")
|
|
2499
|
+
}
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
function getVisiblePages(currentPage, pageCount, maxPageButtons) {
|
|
2503
|
+
if (pageCount <= maxPageButtons) return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
2504
|
+
const half = Math.floor(maxPageButtons / 2);
|
|
2505
|
+
const start = Math.min(Math.max(1, currentPage - half), pageCount - maxPageButtons + 1);
|
|
2506
|
+
return Array.from({ length: maxPageButtons }, (_, index) => start + index);
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
// src/features/query/query-controls.tsx
|
|
2510
|
+
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2511
|
+
function KeepSearchInput({
|
|
2512
|
+
value: controlledValue,
|
|
2513
|
+
defaultValue = "",
|
|
2514
|
+
debounceMs = 300,
|
|
2515
|
+
onValueChange,
|
|
2516
|
+
"aria-label": ariaLabel,
|
|
2517
|
+
placeholder,
|
|
2518
|
+
...props
|
|
2519
|
+
}) {
|
|
2520
|
+
const view = useKeepSearchInput({ controlledValue, defaultValue, debounceMs, onValueChange });
|
|
2521
|
+
return /* @__PURE__ */ jsx10(
|
|
2522
|
+
"input",
|
|
2523
|
+
{
|
|
2524
|
+
...props,
|
|
2525
|
+
"data-keepkit": "search-input",
|
|
2526
|
+
"data-keep-action": "search",
|
|
2527
|
+
type: "search",
|
|
2528
|
+
value: view.value,
|
|
2529
|
+
"data-state": view.value ? "active" : "idle",
|
|
2530
|
+
"data-disabled": props.disabled ? "true" : void 0,
|
|
2531
|
+
"aria-label": ariaLabel ?? view.label,
|
|
2532
|
+
placeholder: placeholder ?? view.label,
|
|
2533
|
+
onChange: view.change
|
|
2534
|
+
}
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
function KeepSortSelect({
|
|
2538
|
+
value: controlledValue,
|
|
2539
|
+
defaultValue = "updatedAt:desc",
|
|
2540
|
+
onValueChange,
|
|
2541
|
+
"aria-label": ariaLabel,
|
|
2542
|
+
children,
|
|
2543
|
+
...props
|
|
2544
|
+
}) {
|
|
2545
|
+
const view = useKeepSortSelect({ controlledValue, defaultValue, onValueChange });
|
|
2546
|
+
const options = children ?? /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
2547
|
+
/* @__PURE__ */ jsx10("option", { value: "updatedAt:desc", children: view.labels.updatedNewest }),
|
|
2548
|
+
/* @__PURE__ */ jsx10("option", { value: "updatedAt:asc", children: view.labels.updatedOldest }),
|
|
2549
|
+
/* @__PURE__ */ jsx10("option", { value: "savedAt:desc", children: view.labels.savedNewest }),
|
|
2550
|
+
/* @__PURE__ */ jsx10("option", { value: "savedAt:asc", children: view.labels.savedOldest })
|
|
2551
|
+
] });
|
|
2552
|
+
return /* @__PURE__ */ jsx10(
|
|
2553
|
+
"select",
|
|
2554
|
+
{
|
|
2555
|
+
...props,
|
|
2556
|
+
"data-keepkit": "sort-select",
|
|
2557
|
+
"data-keep-action": "sort",
|
|
2558
|
+
value: view.value,
|
|
2559
|
+
"data-state": "selected",
|
|
2560
|
+
"data-disabled": props.disabled ? "true" : void 0,
|
|
2561
|
+
"aria-label": ariaLabel ?? view.labels.sort,
|
|
2562
|
+
onChange: view.change,
|
|
2563
|
+
children: options
|
|
2564
|
+
}
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2567
|
+
function KeepPagination({
|
|
2568
|
+
totalCount,
|
|
2569
|
+
pageSize,
|
|
2570
|
+
page = 1,
|
|
2571
|
+
maxPageButtons = 7,
|
|
2572
|
+
onPageChange,
|
|
2573
|
+
render,
|
|
2574
|
+
...props
|
|
2575
|
+
}) {
|
|
2576
|
+
const view = useKeepPagination({ totalCount, pageSize, page, maxPageButtons, onPageChange });
|
|
2577
|
+
const navProps = {
|
|
2578
|
+
...props,
|
|
2579
|
+
"data-keepkit": "pagination",
|
|
2580
|
+
"aria-label": props["aria-label"] ?? view.labels.pagination,
|
|
2581
|
+
"data-state": view.pageCount > 1 ? "active" : "idle"
|
|
2582
|
+
};
|
|
2583
|
+
if (render)
|
|
2584
|
+
return /* @__PURE__ */ jsx10("nav", { ...navProps, children: render({ page: view.currentPage, pageCount: view.pageCount, goToPage: view.goToPage }) });
|
|
2585
|
+
return /* @__PURE__ */ jsxs7("nav", { ...navProps, children: [
|
|
2586
|
+
/* @__PURE__ */ jsx10(
|
|
2587
|
+
"button",
|
|
2588
|
+
{
|
|
2589
|
+
type: "button",
|
|
2590
|
+
"data-keep-action": "previous-page",
|
|
2591
|
+
onClick: () => view.goToPage(view.currentPage - 1),
|
|
2592
|
+
disabled: view.currentPage <= 1,
|
|
2593
|
+
children: view.labels.previous
|
|
2594
|
+
}
|
|
2595
|
+
),
|
|
2596
|
+
view.visiblePages.map((nextPage) => /* @__PURE__ */ jsx10(
|
|
2597
|
+
"button",
|
|
2598
|
+
{
|
|
2599
|
+
type: "button",
|
|
2600
|
+
"data-keep-action": "select-page",
|
|
2601
|
+
"aria-current": nextPage === view.currentPage ? "page" : void 0,
|
|
2602
|
+
"aria-label": `${view.labels.page} ${nextPage}`,
|
|
2603
|
+
onClick: () => view.goToPage(nextPage),
|
|
2604
|
+
children: nextPage
|
|
2605
|
+
},
|
|
2606
|
+
nextPage
|
|
2607
|
+
)),
|
|
2608
|
+
/* @__PURE__ */ jsx10(
|
|
2609
|
+
"button",
|
|
2610
|
+
{
|
|
2611
|
+
type: "button",
|
|
2612
|
+
"data-keep-action": "next-page",
|
|
2613
|
+
onClick: () => view.goToPage(view.currentPage + 1),
|
|
2614
|
+
disabled: view.currentPage >= view.pageCount,
|
|
2615
|
+
children: view.labels.next
|
|
2616
|
+
}
|
|
2617
|
+
)
|
|
2618
|
+
] });
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// src/features/collection/hooks/useKeepCollection.ts
|
|
2622
|
+
import { useKeepList as useKeepList3 } from "@keepkit/core/react";
|
|
2623
|
+
import { useMemo as useMemo3, useState as useState6 } from "react";
|
|
2624
|
+
function useKeepCollection({
|
|
2625
|
+
query,
|
|
2626
|
+
pageSize,
|
|
2627
|
+
urlSync,
|
|
2628
|
+
urlAdapter,
|
|
2629
|
+
features
|
|
2630
|
+
}) {
|
|
2631
|
+
const enabled = {
|
|
2632
|
+
search: true,
|
|
2633
|
+
sort: true,
|
|
2634
|
+
pagination: true,
|
|
2635
|
+
tagFilter: false,
|
|
2636
|
+
bulkActions: false,
|
|
2637
|
+
...features
|
|
2638
|
+
};
|
|
2639
|
+
const [searchValue, setSearchValue] = useState6(query.search?.query ?? "");
|
|
2640
|
+
const [sort, setSort] = useState6(query.sort ?? { by: "updatedAt", direction: "desc" });
|
|
2641
|
+
const [activeTags, setActiveTags] = useState6(query.tags ?? []);
|
|
2642
|
+
const [page, setPage] = useState6(query.pagination?.page ?? 1);
|
|
2643
|
+
const resolvedPageSize = query.pagination?.pageSize ?? pageSize;
|
|
2644
|
+
const resolvedQuery = useMemo3(
|
|
2645
|
+
() => ({
|
|
2646
|
+
...query,
|
|
2647
|
+
search: enabled.search ? { ...query.search, query: searchValue } : query.search,
|
|
2648
|
+
sort: enabled.sort ? sort : query.sort,
|
|
2649
|
+
tags: activeTags.length > 0 ? activeTags : void 0,
|
|
2650
|
+
pagination: enabled.pagination ? { ...query.pagination, page, pageSize: resolvedPageSize } : query.pagination
|
|
2651
|
+
}),
|
|
2652
|
+
[activeTags, enabled.pagination, enabled.search, enabled.sort, page, query, resolvedPageSize, searchValue, sort]
|
|
2653
|
+
);
|
|
2654
|
+
useKeepUrlSync({
|
|
2086
2655
|
enabled: Boolean(urlSync),
|
|
2087
2656
|
query: resolvedQuery,
|
|
2088
2657
|
onQueryChange: (nextOrUpdater) => {
|
|
2089
2658
|
const next = typeof nextOrUpdater === "function" ? nextOrUpdater(resolvedQuery) : nextOrUpdater;
|
|
2090
2659
|
setSearchValue(next.search?.query ?? "");
|
|
2091
2660
|
setSort(next.sort ?? { by: "updatedAt", direction: "desc" });
|
|
2092
|
-
|
|
2661
|
+
setActiveTags(next.tags ?? []);
|
|
2093
2662
|
setPage(next.pagination?.page ?? 1);
|
|
2094
2663
|
},
|
|
2095
2664
|
options: typeof urlSync === "object" ? urlSync : {},
|
|
2096
2665
|
adapter: urlAdapter
|
|
2097
2666
|
});
|
|
2098
|
-
const list =
|
|
2667
|
+
const list = useKeepList3(resolvedQuery);
|
|
2668
|
+
const allState = useKeepList3({});
|
|
2099
2669
|
return {
|
|
2100
2670
|
enabled,
|
|
2101
2671
|
searchValue,
|
|
2102
2672
|
sortValue: sortToValue(sort),
|
|
2103
|
-
|
|
2673
|
+
activeTags,
|
|
2104
2674
|
resolvedPageSize,
|
|
2105
2675
|
resolvedQuery,
|
|
2106
2676
|
list,
|
|
2677
|
+
allState,
|
|
2107
2678
|
setSearchValue: (value) => {
|
|
2108
2679
|
setSearchValue(value);
|
|
2109
2680
|
setPage(1);
|
|
@@ -2113,182 +2684,54 @@ function useKeepCollection({
|
|
|
2113
2684
|
setPage(1);
|
|
2114
2685
|
},
|
|
2115
2686
|
setTag: (value) => {
|
|
2116
|
-
|
|
2687
|
+
setActiveTags(value ? [value] : []);
|
|
2688
|
+
setPage(1);
|
|
2689
|
+
},
|
|
2690
|
+
removeTag: (tagToRemove) => {
|
|
2691
|
+
setActiveTags((current) => current.filter((tag) => tag !== tagToRemove));
|
|
2692
|
+
setPage(1);
|
|
2693
|
+
},
|
|
2694
|
+
clearFilters: () => {
|
|
2695
|
+
setSearchValue("");
|
|
2696
|
+
setActiveTags([]);
|
|
2117
2697
|
setPage(1);
|
|
2118
2698
|
},
|
|
2119
2699
|
setPage
|
|
2120
2700
|
};
|
|
2121
2701
|
}
|
|
2122
2702
|
|
|
2123
|
-
// src/KeepList.tsx
|
|
2703
|
+
// src/features/collection/KeepList.tsx
|
|
2124
2704
|
import { KeepErrorBoundary } from "@keepkit/core/react";
|
|
2125
|
-
import { isValidElement as
|
|
2126
|
-
|
|
2127
|
-
// src/hooks/useKeepListView.ts
|
|
2128
|
-
import { useKeepList as useKeepList3 } from "@keepkit/core/react";
|
|
2129
|
-
function useKeepListView(query) {
|
|
2130
|
-
return {
|
|
2131
|
-
state: useKeepList3(query),
|
|
2132
|
-
labels: {
|
|
2133
|
-
loading: useUiLabel("loadingItems"),
|
|
2134
|
-
empty: useUiLabel("noItems"),
|
|
2135
|
-
error: useUiLabel("errorItems")
|
|
2136
|
-
}
|
|
2137
|
-
};
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
// src/hooks/useRovingTabIndex.ts
|
|
2141
|
-
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
2142
|
-
function useRovingTabIndex() {
|
|
2143
|
-
const ref = useRef4(null);
|
|
2144
|
-
const getItems = useCallback3(() => {
|
|
2145
|
-
const root = ref.current;
|
|
2146
|
-
if (!root) return [];
|
|
2147
|
-
return Array.from(root.querySelectorAll('[data-keepkit="card"]')).filter(
|
|
2148
|
-
(item) => item.getAttribute("aria-hidden") !== "true" && item.getAttribute("data-roving-disabled") !== "true"
|
|
2149
|
-
);
|
|
2150
|
-
}, []);
|
|
2151
|
-
const syncTabIndices = useCallback3(() => {
|
|
2152
|
-
const items = getItems();
|
|
2153
|
-
if (items.length === 0) return;
|
|
2154
|
-
const activeElement = document.activeElement;
|
|
2155
|
-
const activeItem = items.find((item) => item === activeElement || item.contains(activeElement));
|
|
2156
|
-
const activeIndex = activeItem ? items.indexOf(activeItem) : 0;
|
|
2157
|
-
items.forEach((item, index) => {
|
|
2158
|
-
item.tabIndex = index === activeIndex ? 0 : -1;
|
|
2159
|
-
});
|
|
2160
|
-
}, [getItems]);
|
|
2161
|
-
useEffect3(() => {
|
|
2162
|
-
const root = ref.current;
|
|
2163
|
-
if (!root) return;
|
|
2164
|
-
syncTabIndices();
|
|
2165
|
-
const observer = new MutationObserver(syncTabIndices);
|
|
2166
|
-
observer.observe(root, { childList: true, subtree: true });
|
|
2167
|
-
return () => observer.disconnect();
|
|
2168
|
-
}, [syncTabIndices]);
|
|
2169
|
-
const onFocusCapture = useCallback3(
|
|
2170
|
-
(event) => {
|
|
2171
|
-
if (!(event.target instanceof HTMLElement)) return;
|
|
2172
|
-
const item = event.target.closest('[data-keepkit="card"]');
|
|
2173
|
-
if (!item || !ref.current?.contains(item)) return;
|
|
2174
|
-
getItems().forEach((candidate) => {
|
|
2175
|
-
candidate.tabIndex = candidate === item ? 0 : -1;
|
|
2176
|
-
});
|
|
2177
|
-
},
|
|
2178
|
-
[getItems]
|
|
2179
|
-
);
|
|
2180
|
-
const onKeyDown = useCallback3(
|
|
2181
|
-
(event) => {
|
|
2182
|
-
if (event.defaultPrevented) return;
|
|
2183
|
-
const items = getItems();
|
|
2184
|
-
if (!(event.target instanceof HTMLElement)) return;
|
|
2185
|
-
const current = event.target.closest('[data-keepkit="card"]');
|
|
2186
|
-
if (!current || current !== event.target || !ref.current?.contains(current)) return;
|
|
2187
|
-
const currentIndex = items.indexOf(current);
|
|
2188
|
-
if (currentIndex < 0) return;
|
|
2189
|
-
let nextIndex;
|
|
2190
|
-
if (event.key === "Home") nextIndex = 0;
|
|
2191
|
-
if (event.key === "End") nextIndex = items.length - 1;
|
|
2192
|
-
if (event.key === "ArrowRight" || event.key === "ArrowDown")
|
|
2193
|
-
nextIndex = Math.min(currentIndex + 1, items.length - 1);
|
|
2194
|
-
if (event.key === "ArrowLeft" || event.key === "ArrowUp") nextIndex = Math.max(currentIndex - 1, 0);
|
|
2195
|
-
if (nextIndex === void 0) return;
|
|
2196
|
-
event.preventDefault();
|
|
2197
|
-
if (nextIndex === currentIndex) return;
|
|
2198
|
-
items[nextIndex]?.focus();
|
|
2199
|
-
},
|
|
2200
|
-
[getItems]
|
|
2201
|
-
);
|
|
2202
|
-
return { ref, onKeyDown, onFocusCapture };
|
|
2203
|
-
}
|
|
2705
|
+
import { isValidElement as isValidElement6 } from "react";
|
|
2204
2706
|
|
|
2205
|
-
// src/KeepItemCard.tsx
|
|
2707
|
+
// src/features/item/KeepItemCard.tsx
|
|
2206
2708
|
import {
|
|
2207
2709
|
createContext as createContext3,
|
|
2208
2710
|
createElement as createElement2,
|
|
2209
|
-
isValidElement as
|
|
2711
|
+
isValidElement as isValidElement4,
|
|
2210
2712
|
useContext as useContext3,
|
|
2211
|
-
useEffect as
|
|
2212
|
-
useState as
|
|
2713
|
+
useEffect as useEffect5,
|
|
2714
|
+
useState as useState8
|
|
2213
2715
|
} from "react";
|
|
2214
2716
|
|
|
2215
|
-
// src/hooks/
|
|
2216
|
-
|
|
2217
|
-
function useKeepItemCard(options) {
|
|
2218
|
-
const {
|
|
2219
|
-
item,
|
|
2220
|
-
title,
|
|
2221
|
-
getTitle,
|
|
2222
|
-
getImageProps,
|
|
2223
|
-
href: hrefOption,
|
|
2224
|
-
linkTargetAttribute,
|
|
2225
|
-
linkRel,
|
|
2226
|
-
onRemoveError,
|
|
2227
|
-
onRemoved
|
|
2228
|
-
} = options;
|
|
2229
|
-
const itemState = useKeepItem2(item);
|
|
2230
|
-
const emitFeedback = useKeepUiFeedback();
|
|
2231
|
-
const removedMessage = useUiLabel("removedMessage");
|
|
2232
|
-
const restoredMessage = useUiLabel("restoredMessage");
|
|
2233
|
-
const undoLabel = useUiLabel("undo");
|
|
2234
|
-
const resolvedTitle = typeof title === "function" ? title(item) : title ?? getTitle?.(item) ?? getMetaTitle(item.meta) ?? item.id;
|
|
2235
|
-
const imageProps = getImageProps?.(item, resolvedTitle);
|
|
2236
|
-
const href = typeof hrefOption === "function" ? hrefOption(item) : hrefOption;
|
|
2237
|
-
const isAvailable = item.status === void 0 || item.status === "available";
|
|
2238
|
-
const isExternalLink = href ? /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(href) : false;
|
|
2239
|
-
const statusLabelKey = item.status && item.status !== "available" ? getStatusLabelKey(item.status) : "statusUnknown";
|
|
2240
|
-
const unavailableLabel = useUiLabel(statusLabelKey);
|
|
2241
|
-
const remove = async () => {
|
|
2242
|
-
const wasSaved = itemState.isSaved;
|
|
2243
|
-
try {
|
|
2244
|
-
await itemState.removeWithUndo();
|
|
2245
|
-
onRemoved?.(item);
|
|
2246
|
-
if (!wasSaved) return;
|
|
2247
|
-
emitFeedback({
|
|
2248
|
-
type: "item-removed",
|
|
2249
|
-
item,
|
|
2250
|
-
message: removedMessage,
|
|
2251
|
-
undoLabel,
|
|
2252
|
-
undo: async () => {
|
|
2253
|
-
await itemState.undo();
|
|
2254
|
-
emitFeedback({ type: "item-restored", item, items: [item], message: restoredMessage });
|
|
2255
|
-
}
|
|
2256
|
-
});
|
|
2257
|
-
} catch (cause) {
|
|
2258
|
-
onRemoveError?.(cause);
|
|
2259
|
-
}
|
|
2260
|
-
};
|
|
2261
|
-
const state = {
|
|
2262
|
-
item,
|
|
2263
|
-
isSaved: itemState.isSaved,
|
|
2264
|
-
isMutating: itemState.isMutating,
|
|
2265
|
-
error: itemState.error,
|
|
2266
|
-
remove,
|
|
2267
|
-
status: item.status
|
|
2268
|
-
};
|
|
2717
|
+
// src/features/item/hooks/useKeepItemStatusBadge.ts
|
|
2718
|
+
function useKeepItemStatusBadge(status) {
|
|
2269
2719
|
return {
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
imageProps,
|
|
2274
|
-
href,
|
|
2275
|
-
isAvailable,
|
|
2276
|
-
displayStatus: getDisplayStatus(item.status),
|
|
2277
|
-
resolvedLinkTarget: linkTargetAttribute ?? (isExternalLink ? "_blank" : void 0),
|
|
2278
|
-
resolvedLinkRel: linkRel ?? (isExternalLink ? "noreferrer" : void 0),
|
|
2279
|
-
statusLabel: item.status && item.status !== "available" ? unavailableLabel : void 0,
|
|
2280
|
-
remove,
|
|
2281
|
-
labels: {
|
|
2282
|
-
save: useUiLabel("save"),
|
|
2283
|
-
savedAt: useUiLabel("saved"),
|
|
2284
|
-
error: useUiLabel("error"),
|
|
2285
|
-
remove: useUiLabel("remove"),
|
|
2286
|
-
tags: useUiLabel("tags")
|
|
2287
|
-
}
|
|
2720
|
+
resolvedStatus: getDisplayStatus(status),
|
|
2721
|
+
statusLabel: useUiLabel(getStatusLabelKey(status)),
|
|
2722
|
+
icon: getStatusIcon(status)
|
|
2288
2723
|
};
|
|
2289
2724
|
}
|
|
2725
|
+
function getStatusIcon(status) {
|
|
2726
|
+
if (status === "available") return "check";
|
|
2727
|
+
if (status === "expired") return "clock";
|
|
2728
|
+
if (status === "removed" || status === "deleted") return "ban";
|
|
2729
|
+
return "lock";
|
|
2730
|
+
}
|
|
2290
2731
|
function getStatusLabelKey(status) {
|
|
2291
2732
|
switch (status) {
|
|
2733
|
+
case "available":
|
|
2734
|
+
return "statusAvailable";
|
|
2292
2735
|
case "expired":
|
|
2293
2736
|
return "statusExpired";
|
|
2294
2737
|
case "removed":
|
|
@@ -2297,28 +2740,85 @@ function getStatusLabelKey(status) {
|
|
|
2297
2740
|
return "statusDeleted";
|
|
2298
2741
|
case "private":
|
|
2299
2742
|
return "statusPrivate";
|
|
2300
|
-
|
|
2743
|
+
case "unknown":
|
|
2301
2744
|
return "statusUnknown";
|
|
2745
|
+
case "restricted":
|
|
2746
|
+
return "statusPrivate";
|
|
2302
2747
|
}
|
|
2303
2748
|
}
|
|
2304
2749
|
function getDisplayStatus(status) {
|
|
2305
|
-
if (status ===
|
|
2750
|
+
if (status === "available") return "available";
|
|
2306
2751
|
if (status === "expired") return "expired";
|
|
2307
|
-
if (status === "removed") return "removed";
|
|
2752
|
+
if (status === "removed" || status === "deleted") return "removed";
|
|
2308
2753
|
return "restricted";
|
|
2309
2754
|
}
|
|
2310
2755
|
|
|
2311
|
-
// src/
|
|
2312
|
-
import {
|
|
2313
|
-
|
|
2756
|
+
// src/features/item/KeepItemStatusBadge.tsx
|
|
2757
|
+
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2758
|
+
function KeepItemStatusBadge({ status = "available", label, className, ...props }) {
|
|
2759
|
+
const view = useKeepItemStatusBadge(status);
|
|
2760
|
+
return /* @__PURE__ */ jsxs8(
|
|
2761
|
+
"span",
|
|
2762
|
+
{
|
|
2763
|
+
...props,
|
|
2764
|
+
className,
|
|
2765
|
+
role: "img",
|
|
2766
|
+
"aria-label": props["aria-label"] ?? view.statusLabel,
|
|
2767
|
+
"data-keepkit": "status-badge",
|
|
2768
|
+
"data-status": status,
|
|
2769
|
+
"data-item-status": view.resolvedStatus,
|
|
2770
|
+
children: [
|
|
2771
|
+
/* @__PURE__ */ jsx11(StatusIcon, { name: view.icon }),
|
|
2772
|
+
/* @__PURE__ */ jsx11("span", { "data-status-label": "true", children: label ?? view.statusLabel })
|
|
2773
|
+
]
|
|
2774
|
+
}
|
|
2775
|
+
);
|
|
2776
|
+
}
|
|
2777
|
+
function StatusIcon({ name }) {
|
|
2778
|
+
return /* @__PURE__ */ jsxs8(
|
|
2779
|
+
"svg",
|
|
2780
|
+
{
|
|
2781
|
+
"data-status-icon": name,
|
|
2782
|
+
viewBox: "0 0 24 24",
|
|
2783
|
+
width: "1em",
|
|
2784
|
+
height: "1em",
|
|
2785
|
+
fill: "none",
|
|
2786
|
+
stroke: "currentColor",
|
|
2787
|
+
strokeWidth: "2",
|
|
2788
|
+
strokeLinecap: "round",
|
|
2789
|
+
strokeLinejoin: "round",
|
|
2790
|
+
"aria-hidden": "true",
|
|
2791
|
+
focusable: "false",
|
|
2792
|
+
children: [
|
|
2793
|
+
name === "check" ? /* @__PURE__ */ jsx11("path", { d: "m5 12 4 4L19 6" }) : null,
|
|
2794
|
+
name === "clock" ? /* @__PURE__ */ jsxs8(Fragment6, { children: [
|
|
2795
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "8" }),
|
|
2796
|
+
/* @__PURE__ */ jsx11("path", { d: "M12 7v5l3 2" })
|
|
2797
|
+
] }) : null,
|
|
2798
|
+
name === "ban" ? /* @__PURE__ */ jsxs8(Fragment6, { children: [
|
|
2799
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "8" }),
|
|
2800
|
+
/* @__PURE__ */ jsx11("path", { d: "m6.5 6.5 11 11" })
|
|
2801
|
+
] }) : null,
|
|
2802
|
+
name === "lock" ? /* @__PURE__ */ jsxs8(Fragment6, { children: [
|
|
2803
|
+
/* @__PURE__ */ jsx11("rect", { x: "5", y: "10", width: "14", height: "10", rx: "2" }),
|
|
2804
|
+
/* @__PURE__ */ jsx11("path", { d: "M8 10V7a4 4 0 0 1 8 0v3" })
|
|
2805
|
+
] }) : null
|
|
2806
|
+
]
|
|
2807
|
+
}
|
|
2808
|
+
);
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
// src/features/status/hooks/useKeepStaleNotice.ts
|
|
2812
|
+
import { useKeepContext as useKeepContext3 } from "@keepkit/core/react";
|
|
2813
|
+
import { useState as useState7 } from "react";
|
|
2314
2814
|
function useKeepStaleNotice({ item, onRetry, onRemoved }) {
|
|
2315
|
-
const context =
|
|
2815
|
+
const context = useKeepContext3();
|
|
2316
2816
|
const emitFeedback = useKeepUiFeedback();
|
|
2317
2817
|
const removedMessage = useUiLabel("removedMessage");
|
|
2318
2818
|
const restoredMessage = useUiLabel("restoredMessage");
|
|
2319
2819
|
const undoLabel = useUiLabel("undo");
|
|
2320
|
-
const [isRetrying, setIsRetrying] =
|
|
2321
|
-
const [error, setError] =
|
|
2820
|
+
const [isRetrying, setIsRetrying] = useState7(false);
|
|
2821
|
+
const [error, setError] = useState7(null);
|
|
2322
2822
|
async function retry() {
|
|
2323
2823
|
setError(null);
|
|
2324
2824
|
setIsRetrying(true);
|
|
@@ -2364,7 +2864,7 @@ function useKeepStaleNotice({ item, onRetry, onRemoved }) {
|
|
|
2364
2864
|
};
|
|
2365
2865
|
}
|
|
2366
2866
|
function useKeepPruneStale({ statuses, onPruned }) {
|
|
2367
|
-
const context =
|
|
2867
|
+
const context = useKeepContext3();
|
|
2368
2868
|
const emitFeedback = useKeepUiFeedback();
|
|
2369
2869
|
const staleItems = context.items.filter((item) => item.status && statuses.includes(item.status));
|
|
2370
2870
|
const staleIds = staleItems.map((item) => item.id);
|
|
@@ -2397,54 +2897,8 @@ function useKeepPruneStale({ statuses, onPruned }) {
|
|
|
2397
2897
|
};
|
|
2398
2898
|
}
|
|
2399
2899
|
|
|
2400
|
-
// src/
|
|
2401
|
-
|
|
2402
|
-
return { resolvedStatus: getDisplayStatus2(status), statusLabel: useUiLabel(getStatusLabelKey2(status)) };
|
|
2403
|
-
}
|
|
2404
|
-
function getStatusLabelKey2(status) {
|
|
2405
|
-
switch (status) {
|
|
2406
|
-
case "available":
|
|
2407
|
-
return "statusAvailable";
|
|
2408
|
-
case "expired":
|
|
2409
|
-
return "statusExpired";
|
|
2410
|
-
case "removed":
|
|
2411
|
-
return "statusRemoved";
|
|
2412
|
-
case "deleted":
|
|
2413
|
-
return "statusDeleted";
|
|
2414
|
-
case "private":
|
|
2415
|
-
return "statusPrivate";
|
|
2416
|
-
case "unknown":
|
|
2417
|
-
return "statusUnknown";
|
|
2418
|
-
case "restricted":
|
|
2419
|
-
return "statusPrivate";
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2422
|
-
function getDisplayStatus2(status) {
|
|
2423
|
-
if (status === "available") return "available";
|
|
2424
|
-
if (status === "expired") return "expired";
|
|
2425
|
-
if (status === "removed") return "removed";
|
|
2426
|
-
return "restricted";
|
|
2427
|
-
}
|
|
2428
|
-
|
|
2429
|
-
// src/KeepItemStatusBadge.tsx
|
|
2430
|
-
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2431
|
-
function KeepItemStatusBadge({ status = "available", label, className, ...props }) {
|
|
2432
|
-
const view = useKeepItemStatusBadge(status);
|
|
2433
|
-
return /* @__PURE__ */ jsx7(
|
|
2434
|
-
"span",
|
|
2435
|
-
{
|
|
2436
|
-
...props,
|
|
2437
|
-
className,
|
|
2438
|
-
"data-keepkit": "status-badge",
|
|
2439
|
-
"data-status": status,
|
|
2440
|
-
"data-item-status": view.resolvedStatus,
|
|
2441
|
-
children: label ?? view.statusLabel
|
|
2442
|
-
}
|
|
2443
|
-
);
|
|
2444
|
-
}
|
|
2445
|
-
|
|
2446
|
-
// src/KeepStaleNotice.tsx
|
|
2447
|
-
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2900
|
+
// src/features/status/KeepStaleNotice.tsx
|
|
2901
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2448
2902
|
function KeepStaleNotice({
|
|
2449
2903
|
item,
|
|
2450
2904
|
onRetry,
|
|
@@ -2456,11 +2910,11 @@ function KeepStaleNotice({
|
|
|
2456
2910
|
...props
|
|
2457
2911
|
}) {
|
|
2458
2912
|
const view = useKeepStaleNotice({ item, onRetry, onRemoved });
|
|
2459
|
-
return /* @__PURE__ */
|
|
2460
|
-
/* @__PURE__ */
|
|
2461
|
-
children ?? (item.statusReason ? /* @__PURE__ */
|
|
2462
|
-
/* @__PURE__ */
|
|
2463
|
-
/* @__PURE__ */
|
|
2913
|
+
return /* @__PURE__ */ jsxs9("aside", { ...props, className, "data-keepkit": "stale-notice", "data-state": view.error ? "error" : "stale", children: [
|
|
2914
|
+
/* @__PURE__ */ jsx12(KeepItemStatusBadge, { status: view.status }),
|
|
2915
|
+
children ?? (item.statusReason ? /* @__PURE__ */ jsx12("p", { children: item.statusReason }) : null),
|
|
2916
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2917
|
+
/* @__PURE__ */ jsx12(
|
|
2464
2918
|
"button",
|
|
2465
2919
|
{
|
|
2466
2920
|
type: "button",
|
|
@@ -2470,7 +2924,7 @@ function KeepStaleNotice({
|
|
|
2470
2924
|
children: retryLabel ?? view.labels.retry
|
|
2471
2925
|
}
|
|
2472
2926
|
),
|
|
2473
|
-
/* @__PURE__ */
|
|
2927
|
+
/* @__PURE__ */ jsx12(
|
|
2474
2928
|
"button",
|
|
2475
2929
|
{
|
|
2476
2930
|
type: "button",
|
|
@@ -2481,7 +2935,7 @@ function KeepStaleNotice({
|
|
|
2481
2935
|
}
|
|
2482
2936
|
)
|
|
2483
2937
|
] }),
|
|
2484
|
-
view.error ? /* @__PURE__ */
|
|
2938
|
+
view.error ? /* @__PURE__ */ jsx12("p", { role: "alert", children: view.error instanceof Error ? view.error.message : view.labels.error }) : null
|
|
2485
2939
|
] });
|
|
2486
2940
|
}
|
|
2487
2941
|
function KeepPruneStaleButton({
|
|
@@ -2493,7 +2947,7 @@ function KeepPruneStaleButton({
|
|
|
2493
2947
|
...props
|
|
2494
2948
|
}) {
|
|
2495
2949
|
const view = useKeepPruneStale({ statuses, onPruned });
|
|
2496
|
-
return /* @__PURE__ */
|
|
2950
|
+
return /* @__PURE__ */ jsx12(
|
|
2497
2951
|
"button",
|
|
2498
2952
|
{
|
|
2499
2953
|
...props,
|
|
@@ -2509,8 +2963,104 @@ function KeepPruneStaleButton({
|
|
|
2509
2963
|
);
|
|
2510
2964
|
}
|
|
2511
2965
|
|
|
2512
|
-
// src/
|
|
2513
|
-
import {
|
|
2966
|
+
// src/features/item/hooks/useKeepItemCard.ts
|
|
2967
|
+
import { useKeepItem as useKeepItem2 } from "@keepkit/core/react";
|
|
2968
|
+
function useKeepItemCard(options) {
|
|
2969
|
+
const {
|
|
2970
|
+
item,
|
|
2971
|
+
title,
|
|
2972
|
+
getTitle,
|
|
2973
|
+
getImageProps,
|
|
2974
|
+
href: hrefOption,
|
|
2975
|
+
linkTargetAttribute,
|
|
2976
|
+
linkRel,
|
|
2977
|
+
onRemoveError,
|
|
2978
|
+
onRemoved
|
|
2979
|
+
} = options;
|
|
2980
|
+
const itemState = useKeepItem2(item);
|
|
2981
|
+
const emitFeedback = useKeepUiFeedback();
|
|
2982
|
+
const removedMessage = useUiLabel("removedMessage");
|
|
2983
|
+
const restoredMessage = useUiLabel("restoredMessage");
|
|
2984
|
+
const undoLabel = useUiLabel("undo");
|
|
2985
|
+
const resolvedTitle = typeof title === "function" ? title(item) : title ?? getTitle?.(item) ?? getMetaTitle(item.meta) ?? item.id;
|
|
2986
|
+
const imageProps = getImageProps?.(item, resolvedTitle);
|
|
2987
|
+
const href = typeof hrefOption === "function" ? hrefOption(item) : hrefOption;
|
|
2988
|
+
const isAvailable = item.status === void 0 || item.status === "available";
|
|
2989
|
+
const isExternalLink = href ? /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(href) : false;
|
|
2990
|
+
const statusLabelKey = item.status && item.status !== "available" ? getStatusLabelKey2(item.status) : "statusUnknown";
|
|
2991
|
+
const unavailableLabel = useUiLabel(statusLabelKey);
|
|
2992
|
+
const remove = async () => {
|
|
2993
|
+
const wasSaved = itemState.isSaved;
|
|
2994
|
+
try {
|
|
2995
|
+
await itemState.removeWithUndo();
|
|
2996
|
+
onRemoved?.(item);
|
|
2997
|
+
if (!wasSaved) return;
|
|
2998
|
+
emitFeedback({
|
|
2999
|
+
type: "item-removed",
|
|
3000
|
+
item,
|
|
3001
|
+
message: removedMessage,
|
|
3002
|
+
undoLabel,
|
|
3003
|
+
undo: async () => {
|
|
3004
|
+
await itemState.undo();
|
|
3005
|
+
emitFeedback({ type: "item-restored", item, items: [item], message: restoredMessage });
|
|
3006
|
+
}
|
|
3007
|
+
});
|
|
3008
|
+
} catch (cause) {
|
|
3009
|
+
onRemoveError?.(cause);
|
|
3010
|
+
}
|
|
3011
|
+
};
|
|
3012
|
+
const state = {
|
|
3013
|
+
item,
|
|
3014
|
+
isSaved: itemState.isSaved,
|
|
3015
|
+
isMutating: itemState.isMutating,
|
|
3016
|
+
error: itemState.error,
|
|
3017
|
+
remove,
|
|
3018
|
+
status: item.status
|
|
3019
|
+
};
|
|
3020
|
+
return {
|
|
3021
|
+
itemState,
|
|
3022
|
+
state,
|
|
3023
|
+
resolvedTitle,
|
|
3024
|
+
imageProps,
|
|
3025
|
+
href,
|
|
3026
|
+
isAvailable,
|
|
3027
|
+
displayStatus: getDisplayStatus2(item.status),
|
|
3028
|
+
resolvedLinkTarget: linkTargetAttribute ?? (isExternalLink ? "_blank" : void 0),
|
|
3029
|
+
resolvedLinkRel: linkRel ?? (isExternalLink ? "noreferrer" : void 0),
|
|
3030
|
+
statusLabel: item.status && item.status !== "available" ? unavailableLabel : void 0,
|
|
3031
|
+
remove,
|
|
3032
|
+
labels: {
|
|
3033
|
+
save: useUiLabel("save"),
|
|
3034
|
+
savedAt: useUiLabel("saved"),
|
|
3035
|
+
error: useUiLabel("error"),
|
|
3036
|
+
remove: useUiLabel("remove"),
|
|
3037
|
+
tags: useUiLabel("tags")
|
|
3038
|
+
}
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
function getStatusLabelKey2(status) {
|
|
3042
|
+
switch (status) {
|
|
3043
|
+
case "expired":
|
|
3044
|
+
return "statusExpired";
|
|
3045
|
+
case "removed":
|
|
3046
|
+
return "statusRemoved";
|
|
3047
|
+
case "deleted":
|
|
3048
|
+
return "statusDeleted";
|
|
3049
|
+
case "private":
|
|
3050
|
+
return "statusPrivate";
|
|
3051
|
+
default:
|
|
3052
|
+
return "statusUnknown";
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
function getDisplayStatus2(status) {
|
|
3056
|
+
if (status === void 0 || status === "available") return "available";
|
|
3057
|
+
if (status === "expired") return "expired";
|
|
3058
|
+
if (status === "removed") return "removed";
|
|
3059
|
+
return "restricted";
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
// src/features/item/KeepItemCard.tsx
|
|
3063
|
+
import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2514
3064
|
var KeepItemCardContext = createContext3(null);
|
|
2515
3065
|
function useKeepItemCardCompound(part) {
|
|
2516
3066
|
const context = useContext3(KeepItemCardContext);
|
|
@@ -2560,7 +3110,7 @@ function KeepItemCardRoot({
|
|
|
2560
3110
|
});
|
|
2561
3111
|
const contextQuery = useKeepSearchQuery();
|
|
2562
3112
|
const searchQuery = highlightQuery ?? contextQuery;
|
|
2563
|
-
const contentChildren = asChild &&
|
|
3113
|
+
const contentChildren = asChild && isValidElement4(children) ? void 0 : children;
|
|
2564
3114
|
function renderLink(content) {
|
|
2565
3115
|
if (!view.href || !view.isAvailable) return content;
|
|
2566
3116
|
const linkProps = {
|
|
@@ -2570,17 +3120,17 @@ function KeepItemCardRoot({
|
|
|
2570
3120
|
onClick: (event) => onOpen?.(item, event),
|
|
2571
3121
|
children: content
|
|
2572
3122
|
};
|
|
2573
|
-
return LinkComponent ? /* @__PURE__ */
|
|
3123
|
+
return LinkComponent ? /* @__PURE__ */ jsx13(LinkComponent, { ...linkProps }) : /* @__PURE__ */ jsx13("a", { ...linkProps });
|
|
2574
3124
|
}
|
|
2575
3125
|
function renderTitle(content) {
|
|
2576
3126
|
if (linkTarget !== "title") return content;
|
|
2577
3127
|
if (view.isAvailable) return renderLink(content);
|
|
2578
|
-
return view.href ? /* @__PURE__ */
|
|
3128
|
+
return view.href ? /* @__PURE__ */ jsx13("span", { "aria-disabled": "true", "data-link-disabled": "true", children: content }) : content;
|
|
2579
3129
|
}
|
|
2580
3130
|
const resolvedImageProps = view.imageProps ? { ...view.imageProps, alt: imageAlt ?? view.imageProps.alt } : void 0;
|
|
2581
3131
|
const imageSource = resolvedImageProps?.src;
|
|
2582
|
-
const [imageStatus, setImageStatus] =
|
|
2583
|
-
|
|
3132
|
+
const [imageStatus, setImageStatus] = useState8(imageSource ? "loading" : "error");
|
|
3133
|
+
useEffect5(() => {
|
|
2584
3134
|
setImageStatus(imageSource ? "loading" : "error");
|
|
2585
3135
|
}, [imageSource]);
|
|
2586
3136
|
let image = null;
|
|
@@ -2598,21 +3148,21 @@ function KeepItemCardRoot({
|
|
|
2598
3148
|
setImageStatus("error");
|
|
2599
3149
|
}
|
|
2600
3150
|
};
|
|
2601
|
-
image = renderImage?.(imagePropsWithHandlers, item) ?? (ImageComponent ? /* @__PURE__ */
|
|
3151
|
+
image = renderImage?.(imagePropsWithHandlers, item) ?? (ImageComponent ? /* @__PURE__ */ jsx13(ImageComponent, { ...imagePropsWithHandlers }) : /* @__PURE__ */ jsx13("img", { ...imagePropsWithHandlers, alt: imagePropsWithHandlers.alt }));
|
|
2602
3152
|
}
|
|
2603
3153
|
const tags = showTags ? item.tags ?? [] : [];
|
|
2604
3154
|
const renderedTags = showTags && tags.length > 0 ? renderTags?.(tags, item) ?? null : null;
|
|
2605
|
-
const meta = showSavedAt ? /* @__PURE__ */
|
|
2606
|
-
/* @__PURE__ */
|
|
3155
|
+
const meta = showSavedAt ? /* @__PURE__ */ jsxs10("div", { "data-card-meta": true, children: [
|
|
3156
|
+
/* @__PURE__ */ jsxs10("span", { children: [
|
|
2607
3157
|
view.labels.savedAt,
|
|
2608
3158
|
":"
|
|
2609
3159
|
] }),
|
|
2610
3160
|
" ",
|
|
2611
|
-
/* @__PURE__ */
|
|
3161
|
+
/* @__PURE__ */ jsx13("time", { dateTime: new Date(item.savedAt).toISOString(), children: formatSavedAt(item.savedAt) })
|
|
2612
3162
|
] }) : null;
|
|
2613
|
-
const error = view.itemState.error ? /* @__PURE__ */
|
|
2614
|
-
const actions = view.statusLabel ? /* @__PURE__ */
|
|
2615
|
-
showSaveButton ? /* @__PURE__ */
|
|
3163
|
+
const error = view.itemState.error ? /* @__PURE__ */ jsx13("p", { role: "alert", children: getErrorMessage2(view.itemState.error, view.labels.error) }) : null;
|
|
3164
|
+
const actions = view.statusLabel ? /* @__PURE__ */ jsx13(KeepStaleNotice, { item, onRetry, onRemoved }) : /* @__PURE__ */ jsxs10(Fragment7, { children: [
|
|
3165
|
+
showSaveButton ? /* @__PURE__ */ jsx13(
|
|
2616
3166
|
KeepButton,
|
|
2617
3167
|
{
|
|
2618
3168
|
item: toKeepButtonItem(item),
|
|
@@ -2620,7 +3170,7 @@ function KeepItemCardRoot({
|
|
|
2620
3170
|
getAriaLabel: (buttonState) => `${buttonState.isSaved ? view.labels.remove : view.labels.save} ${String(view.resolvedTitle)}`
|
|
2621
3171
|
}
|
|
2622
3172
|
) : null,
|
|
2623
|
-
/* @__PURE__ */
|
|
3173
|
+
/* @__PURE__ */ jsx13(
|
|
2624
3174
|
"button",
|
|
2625
3175
|
{
|
|
2626
3176
|
type: "button",
|
|
@@ -2643,18 +3193,18 @@ function KeepItemCardRoot({
|
|
|
2643
3193
|
meta,
|
|
2644
3194
|
error,
|
|
2645
3195
|
actions,
|
|
2646
|
-
renderText: (content) => /* @__PURE__ */
|
|
3196
|
+
renderText: (content) => /* @__PURE__ */ jsx13(KeepHighlight, { query: searchQuery, children: content })
|
|
2647
3197
|
};
|
|
2648
|
-
const defaultBody = /* @__PURE__ */
|
|
2649
|
-
/* @__PURE__ */
|
|
2650
|
-
/* @__PURE__ */
|
|
2651
|
-
/* @__PURE__ */
|
|
3198
|
+
const defaultBody = /* @__PURE__ */ jsxs10(Fragment7, { children: [
|
|
3199
|
+
/* @__PURE__ */ jsx13(KeepItemCardMedia, {}),
|
|
3200
|
+
/* @__PURE__ */ jsx13(KeepItemCardContent, {}),
|
|
3201
|
+
/* @__PURE__ */ jsx13(KeepItemCardActions, {})
|
|
2652
3202
|
] });
|
|
2653
3203
|
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? defaultBody;
|
|
2654
3204
|
const linkedBody = linkTarget === "card" && view.isAvailable ? renderLink(body) : body;
|
|
2655
3205
|
const root = renderRoot(
|
|
2656
3206
|
asChild,
|
|
2657
|
-
|
|
3207
|
+
isValidElement4(children) ? children : void 0,
|
|
2658
3208
|
{
|
|
2659
3209
|
...rootProps,
|
|
2660
3210
|
className,
|
|
@@ -2669,18 +3219,18 @@ function KeepItemCardRoot({
|
|
|
2669
3219
|
linkedBody,
|
|
2670
3220
|
"KeepItemCard"
|
|
2671
3221
|
);
|
|
2672
|
-
return /* @__PURE__ */
|
|
3222
|
+
return /* @__PURE__ */ jsx13(KeepItemCardContext.Provider, { value: compoundValue, children: root });
|
|
2673
3223
|
}
|
|
2674
3224
|
function KeepItemCardMedia({ children, fallback, ...props }) {
|
|
2675
3225
|
const context = useKeepItemCardCompound("Media");
|
|
2676
|
-
return /* @__PURE__ */
|
|
3226
|
+
return /* @__PURE__ */ jsx13("div", { ...props, "data-keep-card-part": "media", "data-media-status": context.imageStatus, "data-aspect-ratio": "1/1", children: children ?? context.image ?? /* @__PURE__ */ jsx13("span", { role: "img", "aria-label": context.fallbackLabel, "data-keep-card-fallback": "true", children: fallback ?? /* @__PURE__ */ jsx13(KeepMediaPlaceholderIcon, {}) }) });
|
|
2677
3227
|
}
|
|
2678
3228
|
function KeepItemCardContent({ children, ...props }) {
|
|
2679
3229
|
const context = useKeepItemCardCompound("Content");
|
|
2680
|
-
return /* @__PURE__ */
|
|
2681
|
-
/* @__PURE__ */
|
|
3230
|
+
return /* @__PURE__ */ jsx13("div", { ...props, "data-keep-card-part": "content", children: children === void 0 ? /* @__PURE__ */ jsxs10(Fragment7, { children: [
|
|
3231
|
+
/* @__PURE__ */ jsx13(KeepItemCardTitle, {}),
|
|
2682
3232
|
context.meta,
|
|
2683
|
-
/* @__PURE__ */
|
|
3233
|
+
/* @__PURE__ */ jsx13(KeepItemCardTags, {}),
|
|
2684
3234
|
context.error
|
|
2685
3235
|
] }) : context.renderText(children) });
|
|
2686
3236
|
}
|
|
@@ -2688,13 +3238,13 @@ function KeepItemCardTitle({ as = "h3", children, ...props }) {
|
|
|
2688
3238
|
const context = useKeepItemCardCompound("Title");
|
|
2689
3239
|
return createElement2(
|
|
2690
3240
|
as,
|
|
2691
|
-
{ ...props, "data-keep-card-part": "title" },
|
|
3241
|
+
{ ...props, "data-keep-card-part": "title", "data-line-clamp": "2" },
|
|
2692
3242
|
context.renderTitle(context.renderText(children ?? context.resolvedTitle))
|
|
2693
3243
|
);
|
|
2694
3244
|
}
|
|
2695
3245
|
function KeepMediaPlaceholderIcon() {
|
|
2696
|
-
return /* @__PURE__ */
|
|
2697
|
-
/* @__PURE__ */
|
|
3246
|
+
return /* @__PURE__ */ jsxs10("svg", { "data-media-fallback-icon": "true", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
3247
|
+
/* @__PURE__ */ jsx13(
|
|
2698
3248
|
"path",
|
|
2699
3249
|
{
|
|
2700
3250
|
d: "M4 5.5A1.5 1.5 0 0 1 5.5 4h13A1.5 1.5 0 0 1 20 5.5v13a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 18.5v-13Z",
|
|
@@ -2702,19 +3252,19 @@ function KeepMediaPlaceholderIcon() {
|
|
|
2702
3252
|
stroke: "currentColor"
|
|
2703
3253
|
}
|
|
2704
3254
|
),
|
|
2705
|
-
/* @__PURE__ */
|
|
2706
|
-
/* @__PURE__ */
|
|
3255
|
+
/* @__PURE__ */ jsx13("circle", { cx: "9", cy: "9", r: "1.5", fill: "currentColor" }),
|
|
3256
|
+
/* @__PURE__ */ jsx13("path", { d: "m5.5 18 4.5-4.5 3 3 2-2L19 18", fill: "none", stroke: "currentColor" })
|
|
2707
3257
|
] });
|
|
2708
3258
|
}
|
|
2709
3259
|
function KeepItemCardTags({ children, ...props }) {
|
|
2710
3260
|
const context = useKeepItemCardCompound("Tags");
|
|
2711
3261
|
if (children === void 0 && context.renderedTags) return context.renderedTags;
|
|
2712
3262
|
if (children === void 0 && context.tags.length === 0) return null;
|
|
2713
|
-
return /* @__PURE__ */
|
|
3263
|
+
return /* @__PURE__ */ jsx13("ul", { ...props, "aria-label": props["aria-label"] ?? context.tagsLabel, "data-keep-card-part": "tags", children: children ?? context.tags.map((tag) => /* @__PURE__ */ jsx13("li", { children: tag }, tag)) });
|
|
2714
3264
|
}
|
|
2715
3265
|
function KeepItemCardActions({ children, ...props }) {
|
|
2716
3266
|
const context = useKeepItemCardCompound("Actions");
|
|
2717
|
-
return /* @__PURE__ */
|
|
3267
|
+
return /* @__PURE__ */ jsx13("div", { ...props, "data-keep-card-part": "actions", children: children ?? context.actions });
|
|
2718
3268
|
}
|
|
2719
3269
|
var KeepItemCard = Object.assign(KeepItemCardRoot, {
|
|
2720
3270
|
Media: KeepItemCardMedia,
|
|
@@ -2722,405 +3272,256 @@ var KeepItemCard = Object.assign(KeepItemCardRoot, {
|
|
|
2722
3272
|
Title: KeepItemCardTitle,
|
|
2723
3273
|
Tags: KeepItemCardTags,
|
|
2724
3274
|
Actions: KeepItemCardActions
|
|
2725
|
-
});
|
|
2726
|
-
function KeepItemCardSkeleton({ layout = "list", ...props }) {
|
|
2727
|
-
return /* @__PURE__ */
|
|
2728
|
-
/* @__PURE__ */
|
|
2729
|
-
/* @__PURE__ */
|
|
2730
|
-
/* @__PURE__ */
|
|
2731
|
-
/* @__PURE__ */
|
|
2732
|
-
/* @__PURE__ */
|
|
2733
|
-
] });
|
|
2734
|
-
}
|
|
2735
|
-
function formatSavedAt(timestamp) {
|
|
2736
|
-
return new Date(timestamp).toISOString().slice(0, 10);
|
|
2737
|
-
}
|
|
2738
|
-
function getErrorMessage2(error, fallback) {
|
|
2739
|
-
return error instanceof Error ? error.message : fallback;
|
|
2740
|
-
}
|
|
2741
|
-
|
|
2742
|
-
// src/KeepList.tsx
|
|
2743
|
-
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2744
|
-
function KeepList(props) {
|
|
2745
|
-
const { fallback, onBoundaryError, boundaryResetKey, ...listProps } = props;
|
|
2746
|
-
const content = /* @__PURE__ */ jsx10(KeepListContent, { ...listProps });
|
|
2747
|
-
if (fallback === void 0 && onBoundaryError === void 0) return content;
|
|
2748
|
-
return /* @__PURE__ */ jsx10(KeepErrorBoundary, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
|
|
2749
|
-
}
|
|
2750
|
-
function KeepListContent({
|
|
2751
|
-
query,
|
|
2752
|
-
children,
|
|
2753
|
-
renderItem,
|
|
2754
|
-
loading,
|
|
2755
|
-
renderLoading,
|
|
2756
|
-
loadingCount = 6,
|
|
2757
|
-
empty,
|
|
2758
|
-
error: errorContent,
|
|
2759
|
-
itemCardProps,
|
|
2760
|
-
layout = "list",
|
|
2761
|
-
asChild = false,
|
|
2762
|
-
onKeyDown,
|
|
2763
|
-
onFocusCapture,
|
|
2764
|
-
className,
|
|
2765
|
-
...rootProps
|
|
2766
|
-
}) {
|
|
2767
|
-
const view = useKeepListView(query);
|
|
2768
|
-
const roving = useRovingTabIndex();
|
|
2769
|
-
const { state } = view;
|
|
2770
|
-
const body = getListBody(state, {
|
|
2771
|
-
children,
|
|
2772
|
-
renderItem,
|
|
2773
|
-
loading: renderLoading !== void 0 ? renderLoading : loading,
|
|
2774
|
-
loadingCount,
|
|
2775
|
-
loadingLabel: view.labels.loading,
|
|
2776
|
-
empty: empty ?? view.labels.empty,
|
|
2777
|
-
error: errorContent ?? view.labels.error,
|
|
2778
|
-
itemCardProps,
|
|
2779
|
-
layout
|
|
2780
|
-
});
|
|
2781
|
-
return renderRoot(
|
|
2782
|
-
asChild,
|
|
2783
|
-
asChild && isValidElement3(children) ? children : void 0,
|
|
2784
|
-
{
|
|
2785
|
-
...rootProps,
|
|
2786
|
-
className,
|
|
2787
|
-
"data-keepkit": "list",
|
|
2788
|
-
"data-layout": layout,
|
|
2789
|
-
"aria-busy": state.isLoading || rootProps["aria-busy"],
|
|
2790
|
-
"data-state": getListState(state),
|
|
2791
|
-
"data-loading": state.isLoading ? "true" : void 0,
|
|
2792
|
-
"data-roving-tabindex": "true",
|
|
2793
|
-
role: rootProps.role ?? "group",
|
|
2794
|
-
ref: roving.ref,
|
|
2795
|
-
onKeyDown: (event) => {
|
|
2796
|
-
onKeyDown?.(event);
|
|
2797
|
-
if (!event.defaultPrevented) roving.onKeyDown(event);
|
|
2798
|
-
},
|
|
2799
|
-
onFocusCapture: (event) => {
|
|
2800
|
-
onFocusCapture?.(event);
|
|
2801
|
-
if (!event.defaultPrevented) roving.onFocusCapture(event);
|
|
2802
|
-
}
|
|
2803
|
-
},
|
|
2804
|
-
/* @__PURE__ */ jsx10(KeepSearchQueryProvider, { query: query?.search?.query, children: body }),
|
|
2805
|
-
"KeepList"
|
|
2806
|
-
);
|
|
2807
|
-
}
|
|
2808
|
-
function getListState(state) {
|
|
2809
|
-
if (state.error && state.items.length === 0) return "error";
|
|
2810
|
-
if (state.isLoading && !state.isHydrated) return "loading";
|
|
2811
|
-
if (state.isHydrated && state.items.length === 0) return "empty";
|
|
2812
|
-
return "ready";
|
|
2813
|
-
}
|
|
2814
|
-
function getListBody(state, options) {
|
|
2815
|
-
if (state.error && state.items.length === 0) return resolveContent(options.error, state);
|
|
2816
|
-
if (state.isLoading && !state.isHydrated) {
|
|
2817
|
-
if (options.loading !== void 0) return resolveContent(options.loading, state);
|
|
2818
|
-
const count = Number.isFinite(options.loadingCount) ? Math.max(0, Math.floor(options.loadingCount)) : 6;
|
|
2819
|
-
return /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
2820
|
-
/* @__PURE__ */ jsx10("span", { role: "status", "data-keepkit": "loading-label", children: options.loadingLabel }),
|
|
2821
|
-
/* @__PURE__ */ jsx10("ul", { "data-keepkit": "skeleton-list", "data-layout": options.layout, children: Array.from({ length: count }, (_, index) => (
|
|
2822
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: Static loading placeholders never reorder.
|
|
2823
|
-
/* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(KeepItemCardSkeleton, { layout: options.layout }) }, index)
|
|
2824
|
-
)) })
|
|
2825
|
-
] });
|
|
2826
|
-
}
|
|
2827
|
-
if (state.isHydrated && state.items.length === 0) return resolveContent(options.empty, state);
|
|
2828
|
-
if (typeof options.children === "function") return options.children(state);
|
|
2829
|
-
if (options.children !== void 0 && !isValidElement3(options.children)) return options.children;
|
|
2830
|
-
return /* @__PURE__ */ jsx10("ul", { "data-layout": options.layout, children: state.items.map(
|
|
2831
|
-
(item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
|
|
2832
|
-
) });
|
|
3275
|
+
});
|
|
3276
|
+
function KeepItemCardSkeleton({ layout = "list", ...props }) {
|
|
3277
|
+
return /* @__PURE__ */ jsxs10("article", { ...props, "aria-hidden": "true", "data-keepkit": "card-skeleton", "data-layout": layout, "data-state": "loading", children: [
|
|
3278
|
+
/* @__PURE__ */ jsx13("span", { "data-skeleton-part": "media" }),
|
|
3279
|
+
/* @__PURE__ */ jsx13("span", { "data-skeleton-part": "title" }),
|
|
3280
|
+
/* @__PURE__ */ jsx13("span", { "data-skeleton-part": "meta" }),
|
|
3281
|
+
/* @__PURE__ */ jsx13("span", { "data-skeleton-part": "tag" }),
|
|
3282
|
+
/* @__PURE__ */ jsx13("span", { "data-skeleton-part": "tag" })
|
|
3283
|
+
] });
|
|
2833
3284
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
import { useKeepList as useKeepList4 } from "@keepkit/core/react";
|
|
2840
|
-
import { useCallback as useCallback4, useMemo as useMemo3, useState as useState6 } from "react";
|
|
2841
|
-
function useKeepTagFilter(options) {
|
|
2842
|
-
const { query, controlledValue, defaultValue, onChange, onValueChange } = options;
|
|
2843
|
-
const [uncontrolledValue, setUncontrolledValue] = useState6(defaultValue);
|
|
2844
|
-
const resolvedValue = controlledValue ?? uncontrolledValue;
|
|
2845
|
-
const list = useKeepList4({
|
|
2846
|
-
...query,
|
|
2847
|
-
tags: resolvedValue ? [...query?.tags ?? [], resolvedValue] : query?.tags
|
|
2848
|
-
});
|
|
2849
|
-
const select = useCallback4(
|
|
2850
|
-
(tag) => {
|
|
2851
|
-
if (controlledValue === void 0) setUncontrolledValue(tag);
|
|
2852
|
-
onChange?.(tag);
|
|
2853
|
-
onValueChange?.(tag);
|
|
2854
|
-
},
|
|
2855
|
-
[controlledValue, onChange, onValueChange]
|
|
2856
|
-
);
|
|
2857
|
-
const state = useMemo3(
|
|
2858
|
-
() => ({ tags: list.tags, tagCounts: list.tagCounts, value: resolvedValue, select }),
|
|
2859
|
-
[list.tagCounts, list.tags, resolvedValue, select]
|
|
2860
|
-
);
|
|
2861
|
-
return {
|
|
2862
|
-
state,
|
|
2863
|
-
isLoading: list.isLoading,
|
|
2864
|
-
labels: { all: useUiLabel("allTags"), aria: useUiLabel("filterTags") }
|
|
2865
|
-
};
|
|
3285
|
+
function formatSavedAt(timestamp) {
|
|
3286
|
+
return new Date(timestamp).toISOString().slice(0, 10);
|
|
3287
|
+
}
|
|
3288
|
+
function getErrorMessage2(error, fallback) {
|
|
3289
|
+
return error instanceof Error ? error.message : fallback;
|
|
2866
3290
|
}
|
|
2867
3291
|
|
|
2868
|
-
// src/
|
|
2869
|
-
import {
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
onValueChange,
|
|
2876
|
-
allLabel,
|
|
2877
|
-
ariaLabel,
|
|
2878
|
-
renderTag,
|
|
2879
|
-
render,
|
|
3292
|
+
// src/features/status/KeepEmptyState.tsx
|
|
3293
|
+
import { isValidElement as isValidElement5 } from "react";
|
|
3294
|
+
import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3295
|
+
function KeepEmptyState({
|
|
3296
|
+
title,
|
|
3297
|
+
description,
|
|
3298
|
+
action,
|
|
2880
3299
|
children,
|
|
3300
|
+
variant,
|
|
3301
|
+
onClearFilters,
|
|
2881
3302
|
asChild = false,
|
|
2882
3303
|
className,
|
|
2883
3304
|
...rootProps
|
|
2884
3305
|
}) {
|
|
2885
|
-
const
|
|
2886
|
-
const
|
|
2887
|
-
const
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
children: allLabel ?? view.labels.all
|
|
2897
|
-
}
|
|
2898
|
-
),
|
|
2899
|
-
view.state.tags.map((tag) => /* @__PURE__ */ jsxs7(
|
|
2900
|
-
"button",
|
|
2901
|
-
{
|
|
2902
|
-
type: "button",
|
|
2903
|
-
"data-keep-action": "filter-tag",
|
|
2904
|
-
"aria-pressed": view.state.value === tag,
|
|
2905
|
-
onClick: () => view.state.select(tag),
|
|
2906
|
-
children: [
|
|
2907
|
-
renderTag ? renderTag(tag, view.state.tagCounts[tag] ?? 0, view.state.value === tag) : tag,
|
|
2908
|
-
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2909
|
-
" (",
|
|
2910
|
-
view.state.tagCounts[tag] ?? 0,
|
|
2911
|
-
")"
|
|
2912
|
-
] })
|
|
2913
|
-
]
|
|
2914
|
-
},
|
|
2915
|
-
tag
|
|
2916
|
-
))
|
|
3306
|
+
const resolvedVariant = variant ?? "empty-storage";
|
|
3307
|
+
const defaultTitle = useUiLabel(resolvedVariant === "empty-filtered" ? "noFilteredItems" : "noItems");
|
|
3308
|
+
const defaultDescription = useUiLabel(
|
|
3309
|
+
resolvedVariant === "empty-filtered" ? "noFilteredItemsDescription" : "emptyStorageDescription"
|
|
3310
|
+
);
|
|
3311
|
+
const clearLabel = useUiLabel("clearFilters");
|
|
3312
|
+
const contentChildren = asChild && isValidElement5(children) ? void 0 : children;
|
|
3313
|
+
const body = contentChildren ?? /* @__PURE__ */ jsxs11(Fragment8, { children: [
|
|
3314
|
+
/* @__PURE__ */ jsx14("h2", { children: title ?? defaultTitle }),
|
|
3315
|
+
description ?? /* @__PURE__ */ jsx14("p", { children: defaultDescription }),
|
|
3316
|
+
action ?? (resolvedVariant === "empty-filtered" && onClearFilters ? /* @__PURE__ */ jsx14("button", { type: "button", "data-keep-action": "clear-filters", onClick: onClearFilters, children: clearLabel }) : null)
|
|
2917
3317
|
] });
|
|
2918
3318
|
return renderRoot(
|
|
2919
3319
|
asChild,
|
|
2920
|
-
|
|
2921
|
-
{
|
|
2922
|
-
...rootProps,
|
|
2923
|
-
className,
|
|
2924
|
-
"data-keepkit": "tag-filter",
|
|
2925
|
-
"data-state": view.state.value === void 0 ? "all" : "filtered",
|
|
2926
|
-
"data-loading": view.isLoading ? "true" : void 0
|
|
2927
|
-
},
|
|
3320
|
+
children,
|
|
3321
|
+
{ ...rootProps, className, "data-keepkit": "empty-state", "data-state": variant ?? "empty" },
|
|
2928
3322
|
body,
|
|
2929
|
-
"
|
|
3323
|
+
"KeepEmptyState"
|
|
2930
3324
|
);
|
|
2931
3325
|
}
|
|
2932
3326
|
|
|
2933
|
-
// src/hooks/
|
|
2934
|
-
import {
|
|
2935
|
-
function
|
|
2936
|
-
const { controlledValue, defaultValue, debounceMs, onValueChange } = options;
|
|
2937
|
-
const [uncontrolledValue, setUncontrolledValue] = useState7(defaultValue);
|
|
2938
|
-
const value = controlledValue ?? uncontrolledValue;
|
|
2939
|
-
useEffect5(() => {
|
|
2940
|
-
if (!onValueChange) return;
|
|
2941
|
-
if (debounceMs <= 0) {
|
|
2942
|
-
onValueChange(value);
|
|
2943
|
-
return;
|
|
2944
|
-
}
|
|
2945
|
-
const timer = window.setTimeout(() => onValueChange(value), debounceMs);
|
|
2946
|
-
return () => window.clearTimeout(timer);
|
|
2947
|
-
}, [debounceMs, onValueChange, value]);
|
|
2948
|
-
return {
|
|
2949
|
-
value,
|
|
2950
|
-
label: useUiLabel("search"),
|
|
2951
|
-
change: (event) => {
|
|
2952
|
-
if (controlledValue === void 0) setUncontrolledValue(event.currentTarget.value);
|
|
2953
|
-
}
|
|
2954
|
-
};
|
|
2955
|
-
}
|
|
2956
|
-
function useKeepSortSelect(options) {
|
|
2957
|
-
const { controlledValue, defaultValue, onValueChange } = options;
|
|
2958
|
-
const [uncontrolledValue, setUncontrolledValue] = useState7(defaultValue);
|
|
2959
|
-
const value = controlledValue ?? uncontrolledValue;
|
|
2960
|
-
return {
|
|
2961
|
-
value,
|
|
2962
|
-
change: (event) => {
|
|
2963
|
-
const nextValue = event.currentTarget.value;
|
|
2964
|
-
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
2965
|
-
const [by, direction] = nextValue.split(":");
|
|
2966
|
-
onValueChange?.(nextValue, { by, direction });
|
|
2967
|
-
},
|
|
2968
|
-
labels: {
|
|
2969
|
-
sort: useUiLabel("sort"),
|
|
2970
|
-
updatedNewest: useUiLabel("updatedNewest"),
|
|
2971
|
-
updatedOldest: useUiLabel("updatedOldest"),
|
|
2972
|
-
savedNewest: useUiLabel("savedNewest"),
|
|
2973
|
-
savedOldest: useUiLabel("savedOldest")
|
|
2974
|
-
}
|
|
2975
|
-
};
|
|
2976
|
-
}
|
|
2977
|
-
function useKeepPagination(options) {
|
|
2978
|
-
const { totalCount, pageSize, page, maxPageButtons, onPageChange } = options;
|
|
2979
|
-
const pageCount = Math.max(1, Math.ceil(totalCount / Math.max(1, pageSize)));
|
|
2980
|
-
const currentPage = Math.min(Math.max(1, page), pageCount);
|
|
2981
|
-
const goToPage = (nextPage) => {
|
|
2982
|
-
const next = Math.min(Math.max(1, nextPage), pageCount);
|
|
2983
|
-
onPageChange?.(next, (next - 1) * pageSize);
|
|
2984
|
-
};
|
|
3327
|
+
// src/features/collection/hooks/useKeepListView.ts
|
|
3328
|
+
import { useKeepList as useKeepList4 } from "@keepkit/core/react";
|
|
3329
|
+
function useKeepListView(query) {
|
|
2985
3330
|
return {
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
goToPage,
|
|
2989
|
-
visiblePages: getVisiblePages(currentPage, pageCount, Math.max(1, maxPageButtons)),
|
|
3331
|
+
state: useKeepList4(query),
|
|
3332
|
+
allState: useKeepList4({}),
|
|
2990
3333
|
labels: {
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
pagination: useUiLabel("pagination")
|
|
3334
|
+
loading: useUiLabel("loadingItems"),
|
|
3335
|
+
empty: useUiLabel("noItems"),
|
|
3336
|
+
error: useUiLabel("errorItems")
|
|
2995
3337
|
}
|
|
2996
3338
|
};
|
|
2997
3339
|
}
|
|
2998
|
-
function getVisiblePages(currentPage, pageCount, maxPageButtons) {
|
|
2999
|
-
if (pageCount <= maxPageButtons) return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
3000
|
-
const half = Math.floor(maxPageButtons / 2);
|
|
3001
|
-
const start = Math.min(Math.max(1, currentPage - half), pageCount - maxPageButtons + 1);
|
|
3002
|
-
return Array.from({ length: maxPageButtons }, (_, index) => start + index);
|
|
3003
|
-
}
|
|
3004
3340
|
|
|
3005
|
-
// src/
|
|
3006
|
-
import {
|
|
3007
|
-
function
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
})
|
|
3016
|
-
const
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3341
|
+
// src/features/collection/hooks/useRovingTabIndex.ts
|
|
3342
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef4 } from "react";
|
|
3343
|
+
function useRovingTabIndex() {
|
|
3344
|
+
const ref = useRef4(null);
|
|
3345
|
+
const getItems = useCallback3(() => {
|
|
3346
|
+
const root = ref.current;
|
|
3347
|
+
if (!root) return [];
|
|
3348
|
+
return Array.from(root.querySelectorAll('[data-keepkit="card"]')).filter(
|
|
3349
|
+
(item) => item.getAttribute("aria-hidden") !== "true" && item.getAttribute("data-roving-disabled") !== "true"
|
|
3350
|
+
);
|
|
3351
|
+
}, []);
|
|
3352
|
+
const syncTabIndices = useCallback3(() => {
|
|
3353
|
+
const items = getItems();
|
|
3354
|
+
if (items.length === 0) return;
|
|
3355
|
+
const activeElement = document.activeElement;
|
|
3356
|
+
const activeItem = items.find((item) => item === activeElement || item.contains(activeElement));
|
|
3357
|
+
const activeIndex = activeItem ? items.indexOf(activeItem) : 0;
|
|
3358
|
+
items.forEach((item, index) => {
|
|
3359
|
+
item.tabIndex = index === activeIndex ? 0 : -1;
|
|
3360
|
+
});
|
|
3361
|
+
}, [getItems]);
|
|
3362
|
+
useEffect6(() => {
|
|
3363
|
+
const root = ref.current;
|
|
3364
|
+
if (!root) return;
|
|
3365
|
+
syncTabIndices();
|
|
3366
|
+
const observer = new MutationObserver(syncTabIndices);
|
|
3367
|
+
observer.observe(root, { childList: true, subtree: true });
|
|
3368
|
+
return () => observer.disconnect();
|
|
3369
|
+
}, [syncTabIndices]);
|
|
3370
|
+
const onFocusCapture = useCallback3(
|
|
3371
|
+
(event) => {
|
|
3372
|
+
if (!(event.target instanceof HTMLElement)) return;
|
|
3373
|
+
const item = event.target.closest('[data-keepkit="card"]');
|
|
3374
|
+
if (!item || !ref.current?.contains(item)) return;
|
|
3375
|
+
getItems().forEach((candidate) => {
|
|
3376
|
+
candidate.tabIndex = candidate === item ? 0 : -1;
|
|
3377
|
+
});
|
|
3378
|
+
},
|
|
3379
|
+
[getItems]
|
|
3031
3380
|
);
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
"data-keep-action": "sort",
|
|
3054
|
-
value: view.value,
|
|
3055
|
-
"data-state": "selected",
|
|
3056
|
-
"data-disabled": props.disabled ? "true" : void 0,
|
|
3057
|
-
"aria-label": ariaLabel ?? view.labels.sort,
|
|
3058
|
-
onChange: view.change,
|
|
3059
|
-
children: options
|
|
3060
|
-
}
|
|
3381
|
+
const onKeyDown = useCallback3(
|
|
3382
|
+
(event) => {
|
|
3383
|
+
if (event.defaultPrevented) return;
|
|
3384
|
+
const items = getItems();
|
|
3385
|
+
if (!(event.target instanceof HTMLElement)) return;
|
|
3386
|
+
const current = event.target.closest('[data-keepkit="card"]');
|
|
3387
|
+
if (!current || current !== event.target || !ref.current?.contains(current)) return;
|
|
3388
|
+
const currentIndex = items.indexOf(current);
|
|
3389
|
+
if (currentIndex < 0) return;
|
|
3390
|
+
let nextIndex;
|
|
3391
|
+
if (event.key === "Home") nextIndex = 0;
|
|
3392
|
+
if (event.key === "End") nextIndex = items.length - 1;
|
|
3393
|
+
if (event.key === "ArrowRight" || event.key === "ArrowDown")
|
|
3394
|
+
nextIndex = Math.min(currentIndex + 1, items.length - 1);
|
|
3395
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowUp") nextIndex = Math.max(currentIndex - 1, 0);
|
|
3396
|
+
if (nextIndex === void 0) return;
|
|
3397
|
+
event.preventDefault();
|
|
3398
|
+
if (nextIndex === currentIndex) return;
|
|
3399
|
+
items[nextIndex]?.focus();
|
|
3400
|
+
},
|
|
3401
|
+
[getItems]
|
|
3061
3402
|
);
|
|
3403
|
+
return { ref, onKeyDown, onFocusCapture };
|
|
3062
3404
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3405
|
+
|
|
3406
|
+
// src/features/collection/KeepList.tsx
|
|
3407
|
+
import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3408
|
+
function KeepList(props) {
|
|
3409
|
+
const { fallback, onBoundaryError, boundaryResetKey, ...listProps } = props;
|
|
3410
|
+
const content = /* @__PURE__ */ jsx15(KeepListContent, { ...listProps });
|
|
3411
|
+
if (fallback === void 0 && onBoundaryError === void 0) return content;
|
|
3412
|
+
return /* @__PURE__ */ jsx15(KeepErrorBoundary, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
|
|
3413
|
+
}
|
|
3414
|
+
function KeepListContent({
|
|
3415
|
+
query,
|
|
3416
|
+
children,
|
|
3417
|
+
renderItem,
|
|
3418
|
+
loading,
|
|
3419
|
+
renderLoading,
|
|
3420
|
+
loadingCount = 6,
|
|
3421
|
+
empty,
|
|
3422
|
+
error: errorContent,
|
|
3423
|
+
onClearFilters,
|
|
3424
|
+
itemCardProps,
|
|
3425
|
+
layout = "list",
|
|
3426
|
+
asChild = false,
|
|
3427
|
+
onKeyDown,
|
|
3428
|
+
onFocusCapture,
|
|
3429
|
+
className,
|
|
3430
|
+
...rootProps
|
|
3071
3431
|
}) {
|
|
3072
|
-
const view =
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
),
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3432
|
+
const view = useKeepListView(query);
|
|
3433
|
+
const roving = useRovingTabIndex();
|
|
3434
|
+
const { state } = view;
|
|
3435
|
+
const body = getListBody(state, {
|
|
3436
|
+
children,
|
|
3437
|
+
renderItem,
|
|
3438
|
+
loading: renderLoading !== void 0 ? renderLoading : loading,
|
|
3439
|
+
loadingCount,
|
|
3440
|
+
loadingLabel: view.labels.loading,
|
|
3441
|
+
empty,
|
|
3442
|
+
error: errorContent ?? view.labels.error,
|
|
3443
|
+
onClearFilters,
|
|
3444
|
+
allItemCount: view.allState.totalCount,
|
|
3445
|
+
query,
|
|
3446
|
+
itemCardProps,
|
|
3447
|
+
layout
|
|
3448
|
+
});
|
|
3449
|
+
return renderRoot(
|
|
3450
|
+
asChild,
|
|
3451
|
+
asChild && isValidElement6(children) ? children : void 0,
|
|
3452
|
+
{
|
|
3453
|
+
...rootProps,
|
|
3454
|
+
className,
|
|
3455
|
+
"data-keepkit": "list",
|
|
3456
|
+
"data-layout": layout,
|
|
3457
|
+
"aria-busy": state.isLoading || rootProps["aria-busy"],
|
|
3458
|
+
"data-state": getListState(state),
|
|
3459
|
+
"data-loading": state.isLoading ? "true" : void 0,
|
|
3460
|
+
"data-roving-tabindex": "true",
|
|
3461
|
+
role: rootProps.role ?? "group",
|
|
3462
|
+
ref: roving.ref,
|
|
3463
|
+
onKeyDown: (event) => {
|
|
3464
|
+
onKeyDown?.(event);
|
|
3465
|
+
if (!event.defaultPrevented) roving.onKeyDown(event);
|
|
3101
3466
|
},
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3467
|
+
onFocusCapture: (event) => {
|
|
3468
|
+
onFocusCapture?.(event);
|
|
3469
|
+
if (!event.defaultPrevented) roving.onFocusCapture(event);
|
|
3470
|
+
}
|
|
3471
|
+
},
|
|
3472
|
+
/* @__PURE__ */ jsx15(KeepSearchQueryProvider, { query: query?.search?.query, children: body }),
|
|
3473
|
+
"KeepList"
|
|
3474
|
+
);
|
|
3475
|
+
}
|
|
3476
|
+
function getListState(state) {
|
|
3477
|
+
if (state.error && state.items.length === 0) return "error";
|
|
3478
|
+
if (state.isLoading && !state.isHydrated) return "loading";
|
|
3479
|
+
if (state.isHydrated && state.items.length === 0) return "empty";
|
|
3480
|
+
return "ready";
|
|
3481
|
+
}
|
|
3482
|
+
function getListBody(state, options) {
|
|
3483
|
+
if (state.error && state.items.length === 0) return resolveContent(options.error, state);
|
|
3484
|
+
if (state.isLoading && !state.isHydrated) {
|
|
3485
|
+
if (options.loading !== void 0) return resolveContent(options.loading, state);
|
|
3486
|
+
const count = Number.isFinite(options.loadingCount) ? Math.max(0, Math.floor(options.loadingCount)) : 6;
|
|
3487
|
+
return /* @__PURE__ */ jsxs12(Fragment9, { children: [
|
|
3488
|
+
/* @__PURE__ */ jsx15("span", { role: "status", "data-keepkit": "loading-label", children: options.loadingLabel }),
|
|
3489
|
+
/* @__PURE__ */ jsx15("ul", { "data-keepkit": "skeleton-list", "data-layout": options.layout, children: Array.from({ length: count }, (_, index) => (
|
|
3490
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: Static loading placeholders never reorder.
|
|
3491
|
+
/* @__PURE__ */ jsx15("li", { children: /* @__PURE__ */ jsx15(KeepItemCardSkeleton, { layout: options.layout }) }, index)
|
|
3492
|
+
)) })
|
|
3493
|
+
] });
|
|
3494
|
+
}
|
|
3495
|
+
if (state.isHydrated && state.items.length === 0) {
|
|
3496
|
+
if (options.empty !== void 0) return resolveContent(options.empty, state);
|
|
3497
|
+
return /* @__PURE__ */ jsx15(
|
|
3498
|
+
KeepEmptyState,
|
|
3106
3499
|
{
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
onClick: () => view.goToPage(view.currentPage + 1),
|
|
3110
|
-
disabled: view.currentPage >= view.pageCount,
|
|
3111
|
-
children: view.labels.next
|
|
3500
|
+
variant: options.allItemCount > 0 && hasActiveQueryFilters(options.query) ? "empty-filtered" : "empty-storage",
|
|
3501
|
+
onClearFilters: options.onClearFilters
|
|
3112
3502
|
}
|
|
3113
|
-
)
|
|
3114
|
-
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3505
|
+
if (typeof options.children === "function") return options.children(state);
|
|
3506
|
+
if (options.children !== void 0 && !isValidElement6(options.children)) return options.children;
|
|
3507
|
+
return /* @__PURE__ */ jsx15("ul", { "data-layout": options.layout, children: state.items.map(
|
|
3508
|
+
(item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx15("li", { children: /* @__PURE__ */ jsx15(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
|
|
3509
|
+
) });
|
|
3510
|
+
}
|
|
3511
|
+
function hasActiveQueryFilters(query) {
|
|
3512
|
+
if (!query) return false;
|
|
3513
|
+
return Boolean(
|
|
3514
|
+
query.search?.query?.trim() || query.tags?.some((tag) => tag.trim()) || query.targetType || query.savedBetween || query.filter
|
|
3515
|
+
);
|
|
3115
3516
|
}
|
|
3116
3517
|
|
|
3117
|
-
// src/KeepCollection.tsx
|
|
3118
|
-
import { jsx as
|
|
3518
|
+
// src/features/collection/KeepCollection.tsx
|
|
3519
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3119
3520
|
function KeepCollection(props) {
|
|
3120
3521
|
const { fallback, onBoundaryError, boundaryResetKey, ...collectionProps } = props;
|
|
3121
|
-
const content = /* @__PURE__ */
|
|
3522
|
+
const content = /* @__PURE__ */ jsx16(KeepCollectionContent, { ...collectionProps });
|
|
3122
3523
|
if (fallback === void 0 && onBoundaryError === void 0) return content;
|
|
3123
|
-
return /* @__PURE__ */
|
|
3524
|
+
return /* @__PURE__ */ jsx16(KeepErrorBoundary2, { fallback, onError: onBoundaryError, resetKey: boundaryResetKey, children: content });
|
|
3124
3525
|
}
|
|
3125
3526
|
function KeepCollectionContent({
|
|
3126
3527
|
query = {},
|
|
@@ -3134,13 +3535,14 @@ function KeepCollectionContent({
|
|
|
3134
3535
|
loading,
|
|
3135
3536
|
renderLoading,
|
|
3136
3537
|
loadingCount,
|
|
3538
|
+
activeFilters,
|
|
3137
3539
|
empty,
|
|
3138
3540
|
error,
|
|
3139
3541
|
className,
|
|
3140
3542
|
...rootProps
|
|
3141
3543
|
}) {
|
|
3142
3544
|
const view = useKeepCollection({ query, pageSize, urlSync, urlAdapter, features });
|
|
3143
|
-
return /* @__PURE__ */
|
|
3545
|
+
return /* @__PURE__ */ jsxs13(
|
|
3144
3546
|
"section",
|
|
3145
3547
|
{
|
|
3146
3548
|
...rootProps,
|
|
@@ -3151,12 +3553,22 @@ function KeepCollectionContent({
|
|
|
3151
3553
|
"data-state": getCollectionState(view.list),
|
|
3152
3554
|
"data-loading": view.list.isLoading || view.list.isMutating ? "true" : void 0,
|
|
3153
3555
|
children: [
|
|
3154
|
-
/* @__PURE__ */
|
|
3155
|
-
view.enabled.search ? /* @__PURE__ */
|
|
3156
|
-
view.enabled.sort ? /* @__PURE__ */
|
|
3157
|
-
view.enabled.tagFilter ? /* @__PURE__ */
|
|
3556
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
3557
|
+
view.enabled.search ? /* @__PURE__ */ jsx16(KeepSearchInput, { value: view.searchValue, onValueChange: view.setSearchValue }) : null,
|
|
3558
|
+
view.enabled.sort ? /* @__PURE__ */ jsx16(KeepSortSelect, { value: view.sortValue, onValueChange: view.setSortValue }) : null,
|
|
3559
|
+
view.enabled.tagFilter ? /* @__PURE__ */ jsx16(KeepTagFilter, { query, value: view.activeTags[0], onValueChange: view.setTag }) : null
|
|
3158
3560
|
] }),
|
|
3159
|
-
/* @__PURE__ */
|
|
3561
|
+
activeFilters === void 0 ? /* @__PURE__ */ jsx16(
|
|
3562
|
+
KeepActiveFiltersSummary,
|
|
3563
|
+
{
|
|
3564
|
+
search: view.searchValue,
|
|
3565
|
+
tags: view.activeTags,
|
|
3566
|
+
onSearchChange: view.setSearchValue,
|
|
3567
|
+
onTagChange: view.removeTag,
|
|
3568
|
+
onClear: view.clearFilters
|
|
3569
|
+
}
|
|
3570
|
+
) : resolveContent(activeFilters, view.list),
|
|
3571
|
+
/* @__PURE__ */ jsx16(
|
|
3160
3572
|
KeepList,
|
|
3161
3573
|
{
|
|
3162
3574
|
query: view.resolvedQuery,
|
|
@@ -3166,198 +3578,64 @@ function KeepCollectionContent({
|
|
|
3166
3578
|
loading,
|
|
3167
3579
|
renderLoading,
|
|
3168
3580
|
loadingCount,
|
|
3169
|
-
|
|
3581
|
+
onClearFilters: view.clearFilters,
|
|
3582
|
+
empty: view.list.totalCount === 0 && view.allState.totalCount > 0 ? void 0 : empty,
|
|
3170
3583
|
error
|
|
3171
3584
|
}
|
|
3172
3585
|
),
|
|
3173
|
-
view.enabled.pagination ? /* @__PURE__ */
|
|
3586
|
+
view.enabled.pagination ? /* @__PURE__ */ jsx16(
|
|
3174
3587
|
KeepPagination,
|
|
3175
3588
|
{
|
|
3176
3589
|
totalCount: view.list.totalCount,
|
|
3177
3590
|
pageSize: view.resolvedPageSize,
|
|
3178
3591
|
page: view.list.page,
|
|
3179
3592
|
onPageChange: view.setPage
|
|
3180
|
-
}
|
|
3181
|
-
) : null,
|
|
3182
|
-
view.enabled.bulkActions ? /* @__PURE__ */
|
|
3183
|
-
]
|
|
3184
|
-
}
|
|
3185
|
-
);
|
|
3186
|
-
}
|
|
3187
|
-
function getCollectionState(list) {
|
|
3188
|
-
if (list.error && list.items.length === 0) return "error";
|
|
3189
|
-
if (list.isLoading && !list.isHydrated) return "loading";
|
|
3190
|
-
if (list.isHydrated && list.items.length === 0) return "empty";
|
|
3191
|
-
return "ready";
|
|
3192
|
-
}
|
|
3193
|
-
|
|
3194
|
-
// src/KeepLayout.tsx
|
|
3195
|
-
import { jsx as
|
|
3196
|
-
function KeepLayout({ layout = "list", children, onKeyDown, onFocusCapture, ...props }) {
|
|
3197
|
-
const roving = useRovingTabIndex();
|
|
3198
|
-
return (
|
|
3199
|
-
// biome-ignore lint/a11y/noStaticElementInteractions: The group manages keyboard focus for descendant cards.
|
|
3200
|
-
/* @__PURE__ */
|
|
3201
|
-
"div",
|
|
3202
|
-
{
|
|
3203
|
-
...props,
|
|
3204
|
-
ref: roving.ref,
|
|
3205
|
-
"data-keepkit": "layout",
|
|
3206
|
-
"data-layout": layout,
|
|
3207
|
-
"data-roving-tabindex": "true",
|
|
3208
|
-
role: props.role ?? "group",
|
|
3209
|
-
onKeyDown: (event) => {
|
|
3210
|
-
onKeyDown?.(event);
|
|
3211
|
-
if (!event.defaultPrevented) roving.onKeyDown(event);
|
|
3212
|
-
},
|
|
3213
|
-
onFocusCapture: (event) => {
|
|
3214
|
-
onFocusCapture?.(event);
|
|
3215
|
-
if (!event.defaultPrevented) roving.onFocusCapture(event);
|
|
3216
|
-
},
|
|
3217
|
-
children
|
|
3218
|
-
}
|
|
3219
|
-
)
|
|
3220
|
-
);
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3223
|
-
// src/KeepNoteEditor.tsx
|
|
3224
|
-
import { isValidElement as isValidElement5 } from "react";
|
|
3225
|
-
|
|
3226
|
-
// src/hooks/useKeepNoteEditor.ts
|
|
3227
|
-
import { useKeepItem as useKeepItem3 } from "@keepkit/core/react";
|
|
3228
|
-
import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef5, useState as useState8 } from "react";
|
|
3229
|
-
function useKeepNoteEditor({ item, debounceMs, onSaved, onSaveError }) {
|
|
3230
|
-
const itemState = useKeepItem3(item);
|
|
3231
|
-
const { error, isMutating, item: savedItem, updateNote } = itemState;
|
|
3232
|
-
const [note, setNote] = useState8(item.note ?? "");
|
|
3233
|
-
const baselineNote = savedItem?.note ?? item.note ?? "";
|
|
3234
|
-
const isDirty = note !== baselineNote;
|
|
3235
|
-
const lastSavedNoteRef = useRef5(void 0);
|
|
3236
|
-
useEffect6(() => setNote(baselineNote), [baselineNote]);
|
|
3237
|
-
const save = useCallback5(async () => {
|
|
3238
|
-
const nextNote = note.trim() || void 0;
|
|
3239
|
-
try {
|
|
3240
|
-
await updateNote(nextNote);
|
|
3241
|
-
lastSavedNoteRef.current = note;
|
|
3242
|
-
onSaved?.(nextNote);
|
|
3243
|
-
} catch (cause) {
|
|
3244
|
-
onSaveError?.(cause);
|
|
3245
|
-
throw cause;
|
|
3246
|
-
}
|
|
3247
|
-
}, [note, onSaveError, onSaved, updateNote]);
|
|
3248
|
-
useEffect6(() => {
|
|
3249
|
-
if (!isDirty || debounceMs <= 0 || lastSavedNoteRef.current === note) return;
|
|
3250
|
-
const timer = window.setTimeout(() => void save().catch(() => void 0), debounceMs);
|
|
3251
|
-
return () => window.clearTimeout(timer);
|
|
3252
|
-
}, [debounceMs, isDirty, note, save]);
|
|
3253
|
-
const state = {
|
|
3254
|
-
item,
|
|
3255
|
-
note,
|
|
3256
|
-
setNote,
|
|
3257
|
-
isDirty,
|
|
3258
|
-
isSaving: isMutating,
|
|
3259
|
-
error,
|
|
3260
|
-
save
|
|
3261
|
-
};
|
|
3262
|
-
const submit = (event) => {
|
|
3263
|
-
event.preventDefault();
|
|
3264
|
-
void save().catch(() => void 0);
|
|
3265
|
-
};
|
|
3266
|
-
return {
|
|
3267
|
-
state,
|
|
3268
|
-
submit,
|
|
3269
|
-
handleKeyDown: (event) => {
|
|
3270
|
-
if (event.key !== "Enter" || !event.ctrlKey && !event.metaKey) return;
|
|
3271
|
-
event.preventDefault();
|
|
3272
|
-
void save().catch(() => void 0);
|
|
3273
|
-
},
|
|
3274
|
-
labels: {
|
|
3275
|
-
note: useUiLabel("note"),
|
|
3276
|
-
save: useUiLabel("saveNote"),
|
|
3277
|
-
error: useUiLabel("error")
|
|
3278
|
-
}
|
|
3279
|
-
};
|
|
3280
|
-
}
|
|
3281
|
-
|
|
3282
|
-
// src/KeepNoteEditor.tsx
|
|
3283
|
-
import { Fragment as Fragment7, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3284
|
-
function KeepNoteEditor({
|
|
3285
|
-
item,
|
|
3286
|
-
label,
|
|
3287
|
-
saveLabel,
|
|
3288
|
-
placeholder,
|
|
3289
|
-
debounceMs = 300,
|
|
3290
|
-
onSaved,
|
|
3291
|
-
onSaveError,
|
|
3292
|
-
render,
|
|
3293
|
-
children,
|
|
3294
|
-
asChild = false,
|
|
3295
|
-
className,
|
|
3296
|
-
...formProps
|
|
3297
|
-
}) {
|
|
3298
|
-
const view = useKeepNoteEditor({ item, debounceMs, onSaved, onSaveError });
|
|
3299
|
-
const { error, isDirty, isSaving, note, setNote } = view.state;
|
|
3300
|
-
const contentChildren = asChild && isValidElement5(children) ? void 0 : children;
|
|
3301
|
-
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? /* @__PURE__ */ jsxs10(Fragment7, { children: [
|
|
3302
|
-
/* @__PURE__ */ jsxs10("label", { children: [
|
|
3303
|
-
label ?? view.labels.note,
|
|
3304
|
-
/* @__PURE__ */ jsx15(
|
|
3305
|
-
"textarea",
|
|
3306
|
-
{
|
|
3307
|
-
"data-keep-action": "edit-note",
|
|
3308
|
-
value: note,
|
|
3309
|
-
onChange: (event) => setNote(event.currentTarget.value),
|
|
3310
|
-
placeholder,
|
|
3311
|
-
disabled: isSaving,
|
|
3312
|
-
onKeyDown: view.handleKeyDown
|
|
3313
|
-
}
|
|
3314
|
-
)
|
|
3315
|
-
] }),
|
|
3316
|
-
/* @__PURE__ */ jsx15("button", { type: "submit", "data-keep-action": "save-note", disabled: isSaving, "aria-busy": isSaving, children: saveLabel ?? view.labels.save })
|
|
3317
|
-
] });
|
|
3318
|
-
if (!asChild) {
|
|
3319
|
-
return /* @__PURE__ */ jsxs10(
|
|
3320
|
-
"form",
|
|
3593
|
+
}
|
|
3594
|
+
) : null,
|
|
3595
|
+
view.enabled.bulkActions ? /* @__PURE__ */ jsx16(KeepBulkActions, { query: view.resolvedQuery }) : null
|
|
3596
|
+
]
|
|
3597
|
+
}
|
|
3598
|
+
);
|
|
3599
|
+
}
|
|
3600
|
+
function getCollectionState(list) {
|
|
3601
|
+
if (list.error && list.items.length === 0) return "error";
|
|
3602
|
+
if (list.isLoading && !list.isHydrated) return "loading";
|
|
3603
|
+
if (list.isHydrated && list.items.length === 0) return "empty";
|
|
3604
|
+
return "ready";
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
// src/features/collection/KeepLayout.tsx
|
|
3608
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
3609
|
+
function KeepLayout({ layout = "list", children, onKeyDown, onFocusCapture, ...props }) {
|
|
3610
|
+
const roving = useRovingTabIndex();
|
|
3611
|
+
return (
|
|
3612
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: The group manages keyboard focus for descendant cards.
|
|
3613
|
+
/* @__PURE__ */ jsx17(
|
|
3614
|
+
"div",
|
|
3321
3615
|
{
|
|
3322
|
-
...
|
|
3323
|
-
|
|
3324
|
-
"data-keepkit": "
|
|
3325
|
-
|
|
3326
|
-
"
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3616
|
+
...props,
|
|
3617
|
+
ref: roving.ref,
|
|
3618
|
+
"data-keepkit": "layout",
|
|
3619
|
+
"data-layout": layout,
|
|
3620
|
+
"data-roving-tabindex": "true",
|
|
3621
|
+
role: props.role ?? "group",
|
|
3622
|
+
onKeyDown: (event) => {
|
|
3623
|
+
onKeyDown?.(event);
|
|
3624
|
+
if (!event.defaultPrevented) roving.onKeyDown(event);
|
|
3625
|
+
},
|
|
3626
|
+
onFocusCapture: (event) => {
|
|
3627
|
+
onFocusCapture?.(event);
|
|
3628
|
+
if (!event.defaultPrevented) roving.onFocusCapture(event);
|
|
3629
|
+
},
|
|
3630
|
+
children
|
|
3334
3631
|
}
|
|
3335
|
-
)
|
|
3336
|
-
}
|
|
3337
|
-
return renderRoot(
|
|
3338
|
-
true,
|
|
3339
|
-
isValidElement5(children) ? children : void 0,
|
|
3340
|
-
{
|
|
3341
|
-
...formProps,
|
|
3342
|
-
className,
|
|
3343
|
-
"data-keepkit": "note-editor",
|
|
3344
|
-
onSubmit: view.submit,
|
|
3345
|
-
"aria-busy": isSaving || formProps["aria-busy"],
|
|
3346
|
-
"data-state": error ? "error" : isDirty ? "dirty" : "clean",
|
|
3347
|
-
"data-loading": isSaving ? "true" : void 0,
|
|
3348
|
-
"data-disabled": isSaving ? "true" : void 0
|
|
3349
|
-
},
|
|
3350
|
-
body,
|
|
3351
|
-
"KeepNoteEditor"
|
|
3632
|
+
)
|
|
3352
3633
|
);
|
|
3353
3634
|
}
|
|
3354
|
-
function getErrorMessage3(error, fallback) {
|
|
3355
|
-
return error instanceof Error ? error.message : fallback;
|
|
3356
|
-
}
|
|
3357
3635
|
|
|
3358
|
-
// src/KeepReorderableList.tsx
|
|
3636
|
+
// src/features/collection/KeepReorderableList.tsx
|
|
3359
3637
|
import { useState as useState9 } from "react";
|
|
3360
|
-
import { jsx as
|
|
3638
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
3361
3639
|
function KeepReorderableList({
|
|
3362
3640
|
items,
|
|
3363
3641
|
onReorder,
|
|
@@ -3366,6 +3644,7 @@ function KeepReorderableList({
|
|
|
3366
3644
|
...props
|
|
3367
3645
|
}) {
|
|
3368
3646
|
const [draggedId, setDraggedId] = useState9(null);
|
|
3647
|
+
const [dropTargetIndex, setDropTargetIndex] = useState9(null);
|
|
3369
3648
|
const ids = items.map((item) => item.id);
|
|
3370
3649
|
function commit(nextIds) {
|
|
3371
3650
|
void onReorder(nextIds);
|
|
@@ -3378,7 +3657,19 @@ function KeepReorderableList({
|
|
|
3378
3657
|
next.splice(targetIndex, 0, id);
|
|
3379
3658
|
commit(next);
|
|
3380
3659
|
}
|
|
3381
|
-
|
|
3660
|
+
function moveToInsertion(index, insertionIndex) {
|
|
3661
|
+
const next = [...ids];
|
|
3662
|
+
const [id] = next.splice(index, 1);
|
|
3663
|
+
if (id === void 0) return;
|
|
3664
|
+
const targetIndex = Math.min(
|
|
3665
|
+
Math.max(0, insertionIndex > index ? insertionIndex - 1 : insertionIndex),
|
|
3666
|
+
next.length
|
|
3667
|
+
);
|
|
3668
|
+
next.splice(targetIndex, 0, id);
|
|
3669
|
+
if (next[index] === id) return;
|
|
3670
|
+
commit(next);
|
|
3671
|
+
}
|
|
3672
|
+
return /* @__PURE__ */ jsx18("ul", { ...props, "data-keepkit": "reorderable-list", children: items.map((item, index) => {
|
|
3382
3673
|
const moveUp = () => move(index, index - 1);
|
|
3383
3674
|
const moveDown = () => move(index, index + 1);
|
|
3384
3675
|
const onHandleKeyDown = (event) => {
|
|
@@ -3399,27 +3690,43 @@ function KeepReorderableList({
|
|
|
3399
3690
|
role: "button",
|
|
3400
3691
|
tabIndex: 0,
|
|
3401
3692
|
draggable: true,
|
|
3693
|
+
"data-drag-handle": "true",
|
|
3402
3694
|
"aria-label": itemLabel(item, index),
|
|
3403
3695
|
"aria-grabbed": draggedId === item.id,
|
|
3404
|
-
onDragStart: () =>
|
|
3405
|
-
|
|
3696
|
+
onDragStart: () => {
|
|
3697
|
+
setDraggedId(item.id);
|
|
3698
|
+
setDropTargetIndex(index);
|
|
3699
|
+
},
|
|
3700
|
+
onDragEnd: () => {
|
|
3701
|
+
setDraggedId(null);
|
|
3702
|
+
setDropTargetIndex(null);
|
|
3703
|
+
},
|
|
3406
3704
|
onKeyDown: onHandleKeyDown
|
|
3407
3705
|
}
|
|
3408
3706
|
};
|
|
3409
|
-
return /* @__PURE__ */
|
|
3707
|
+
return /* @__PURE__ */ jsx18(
|
|
3410
3708
|
"li",
|
|
3411
3709
|
{
|
|
3412
3710
|
"data-reorder-index": index,
|
|
3413
3711
|
"data-dragging": draggedId === item.id ? "true" : void 0,
|
|
3712
|
+
"data-drop-target": draggedId && draggedId !== item.id ? dropTargetIndex === index ? "before" : dropTargetIndex === index + 1 ? "after" : void 0 : void 0,
|
|
3414
3713
|
onDragOver: (event) => {
|
|
3415
|
-
if (draggedId
|
|
3714
|
+
if (!draggedId || draggedId === item.id) return;
|
|
3715
|
+
event.preventDefault();
|
|
3716
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
3717
|
+
const insertionIndex = event.clientY > bounds.top + bounds.height / 2 ? index + 1 : index;
|
|
3718
|
+
setDropTargetIndex(insertionIndex);
|
|
3719
|
+
},
|
|
3720
|
+
onDragLeave: (event) => {
|
|
3721
|
+
if (event.currentTarget === event.target) setDropTargetIndex(null);
|
|
3416
3722
|
},
|
|
3417
3723
|
onDrop: (event) => {
|
|
3418
3724
|
event.preventDefault();
|
|
3419
3725
|
if (!draggedId || draggedId === item.id) return;
|
|
3420
3726
|
const sourceIndex = ids.indexOf(draggedId);
|
|
3421
3727
|
setDraggedId(null);
|
|
3422
|
-
|
|
3728
|
+
setDropTargetIndex(null);
|
|
3729
|
+
moveToInsertion(sourceIndex, dropTargetIndex ?? index);
|
|
3423
3730
|
},
|
|
3424
3731
|
children: renderItem(item, state)
|
|
3425
3732
|
},
|
|
@@ -3428,309 +3735,170 @@ function KeepReorderableList({
|
|
|
3428
3735
|
}) });
|
|
3429
3736
|
}
|
|
3430
3737
|
|
|
3431
|
-
// src/
|
|
3432
|
-
import {
|
|
3433
|
-
import { useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
3434
|
-
function useKeepSyncFeedback() {
|
|
3435
|
-
const { syncState } = useKeepContext3();
|
|
3436
|
-
const emitFeedback = useKeepUiFeedback();
|
|
3437
|
-
const completedMessage = useUiLabel("syncSynced");
|
|
3438
|
-
const failedMessage = useUiLabel("syncFailedMessage");
|
|
3439
|
-
const previousStatus = useRef6("idle");
|
|
3440
|
-
useEffect7(() => {
|
|
3441
|
-
const previous = previousStatus.current;
|
|
3442
|
-
previousStatus.current = syncState.status;
|
|
3443
|
-
if (syncState.status === "error" && previous !== "error") {
|
|
3444
|
-
emitFeedback({ type: "sync-failed", error: syncState.error, message: failedMessage });
|
|
3445
|
-
return;
|
|
3446
|
-
}
|
|
3447
|
-
if (syncState.status === "synced" && (previous === "pending" || previous === "syncing" || previous === "conflict" || previous === "error")) {
|
|
3448
|
-
emitFeedback({ type: "sync-completed", message: completedMessage });
|
|
3449
|
-
}
|
|
3450
|
-
}, [completedMessage, emitFeedback, failedMessage, syncState.error, syncState.status]);
|
|
3451
|
-
}
|
|
3452
|
-
|
|
3453
|
-
// src/KeepSyncFeedbackObserver.tsx
|
|
3454
|
-
function KeepSyncFeedbackObserver() {
|
|
3455
|
-
useKeepSyncFeedback();
|
|
3456
|
-
return null;
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
// src/hooks/useKeepSyncRecoveryDialog.ts
|
|
3460
|
-
import { useKeepContext as useKeepContext4 } from "@keepkit/core/react";
|
|
3461
|
-
import { useEffect as useEffect8, useState as useState10 } from "react";
|
|
3462
|
-
function useKeepSyncRecoveryDialog(options) {
|
|
3463
|
-
const { open, onOpenChange, conflicts, onManualMerge } = options;
|
|
3464
|
-
const context = useKeepContext4();
|
|
3465
|
-
const conflictList = conflicts ?? context.syncState.conflicts ?? [];
|
|
3466
|
-
const hasRecovery = conflictList.length > 0 || context.syncState.status === "error" || Boolean(context.error);
|
|
3467
|
-
const [dismissed, setDismissed] = useState10(false);
|
|
3468
|
-
const [busyId, setBusyId] = useState10();
|
|
3469
|
-
const [error, setError] = useState10();
|
|
3470
|
-
useEffect8(() => {
|
|
3471
|
-
if (hasRecovery) setDismissed(false);
|
|
3472
|
-
}, [hasRecovery]);
|
|
3473
|
-
return {
|
|
3474
|
-
conflictList,
|
|
3475
|
-
isOpen: open ?? (hasRecovery && !dismissed),
|
|
3476
|
-
busyId,
|
|
3477
|
-
error,
|
|
3478
|
-
showBackupRecovery: context.syncState.status === "error" || Boolean(context.error),
|
|
3479
|
-
close: () => {
|
|
3480
|
-
setDismissed(true);
|
|
3481
|
-
onOpenChange?.(false);
|
|
3482
|
-
},
|
|
3483
|
-
resolve: async (conflict, resolution) => {
|
|
3484
|
-
setError(void 0);
|
|
3485
|
-
setBusyId(conflict.id);
|
|
3486
|
-
try {
|
|
3487
|
-
const merged = resolution === "manual" ? await onManualMerge?.(conflict) : void 0;
|
|
3488
|
-
if (resolution === "manual" && !merged) throw new Error("A manual merge result is required.");
|
|
3489
|
-
await context.resolveSyncConflict(conflict.id, resolution, merged);
|
|
3490
|
-
} catch (cause) {
|
|
3491
|
-
setError(cause);
|
|
3492
|
-
} finally {
|
|
3493
|
-
setBusyId(void 0);
|
|
3494
|
-
}
|
|
3495
|
-
},
|
|
3496
|
-
labels: {
|
|
3497
|
-
close: useUiLabel("close"),
|
|
3498
|
-
title: useUiLabel("resolveSync"),
|
|
3499
|
-
conflict: useUiLabel("syncConflict"),
|
|
3500
|
-
keepLocal: useUiLabel("keepLocal"),
|
|
3501
|
-
useServer: useUiLabel("useServer"),
|
|
3502
|
-
manualMerge: useUiLabel("manualMerge"),
|
|
3503
|
-
localVersion: useUiLabel("localVersion"),
|
|
3504
|
-
remoteVersion: useUiLabel("remoteVersion"),
|
|
3505
|
-
updatedAt: useUiLabel("updatedAt"),
|
|
3506
|
-
note: useUiLabel("note"),
|
|
3507
|
-
backupRecovery: useUiLabel("backupRecovery"),
|
|
3508
|
-
backupRecoveryDescription: useUiLabel("backupRecoveryDescription"),
|
|
3509
|
-
error: useUiLabel("error")
|
|
3510
|
-
}
|
|
3511
|
-
};
|
|
3512
|
-
}
|
|
3738
|
+
// src/features/editor/KeepNoteEditor.tsx
|
|
3739
|
+
import { isValidElement as isValidElement7 } from "react";
|
|
3513
3740
|
|
|
3514
|
-
// src/
|
|
3515
|
-
import { jsx as
|
|
3516
|
-
function
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
children,
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
})
|
|
3528
|
-
const view = useKeepSyncRecoveryDialog({ open, onOpenChange, conflicts, onManualMerge });
|
|
3529
|
-
if (!view.isOpen) return null;
|
|
3530
|
-
return /* @__PURE__ */ jsxs11(
|
|
3531
|
-
"section",
|
|
3532
|
-
{
|
|
3533
|
-
...props,
|
|
3534
|
-
className,
|
|
3535
|
-
role: "dialog",
|
|
3536
|
-
"aria-modal": "true",
|
|
3537
|
-
"aria-labelledby": "keepkit-sync-recovery-title",
|
|
3538
|
-
"aria-describedby": view.error ? "keepkit-sync-recovery-error" : void 0,
|
|
3539
|
-
"aria-busy": view.busyId !== void 0,
|
|
3540
|
-
"data-keepkit": "sync-recovery",
|
|
3541
|
-
"data-state": view.conflictList.length > 0 ? "conflict" : "error",
|
|
3542
|
-
"data-loading": view.busyId !== void 0 ? "true" : void 0,
|
|
3543
|
-
children: [
|
|
3544
|
-
/* @__PURE__ */ jsxs11("header", { children: [
|
|
3545
|
-
/* @__PURE__ */ jsx17("h2", { id: "keepkit-sync-recovery-title", children: title ?? view.labels.title }),
|
|
3546
|
-
/* @__PURE__ */ jsx17("button", { type: "button", "data-keep-action": "close-dialog", onClick: view.close, "aria-label": view.labels.close, children: view.labels.close })
|
|
3547
|
-
] }),
|
|
3548
|
-
children,
|
|
3549
|
-
view.conflictList.length > 0 ? /* @__PURE__ */ jsxs11("div", { children: [
|
|
3550
|
-
/* @__PURE__ */ jsx17("p", { children: view.labels.conflict }),
|
|
3551
|
-
view.conflictList.map((conflict) => /* @__PURE__ */ jsxs11("article", { "data-conflict-id": conflict.id, children: [
|
|
3552
|
-
/* @__PURE__ */ jsx17("h3", { children: getMetaTitle(conflict.operation.item?.meta) ?? conflict.id }),
|
|
3553
|
-
/* @__PURE__ */ jsxs11("div", { "data-conflict-preview": true, children: [
|
|
3554
|
-
/* @__PURE__ */ jsx17(
|
|
3555
|
-
ConflictPreview,
|
|
3556
|
-
{
|
|
3557
|
-
item: conflict.operation.item,
|
|
3558
|
-
heading: view.labels.localVersion,
|
|
3559
|
-
updatedAtLabel: view.labels.updatedAt,
|
|
3560
|
-
noteLabel: view.labels.note,
|
|
3561
|
-
side: "local"
|
|
3562
|
-
}
|
|
3563
|
-
),
|
|
3564
|
-
/* @__PURE__ */ jsx17(
|
|
3565
|
-
ConflictPreview,
|
|
3566
|
-
{
|
|
3567
|
-
item: conflict.remote,
|
|
3568
|
-
heading: view.labels.remoteVersion,
|
|
3569
|
-
updatedAtLabel: view.labels.updatedAt,
|
|
3570
|
-
noteLabel: view.labels.note,
|
|
3571
|
-
side: "remote"
|
|
3572
|
-
}
|
|
3573
|
-
)
|
|
3574
|
-
] }),
|
|
3575
|
-
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3576
|
-
/* @__PURE__ */ jsx17(
|
|
3577
|
-
"button",
|
|
3578
|
-
{
|
|
3579
|
-
type: "button",
|
|
3580
|
-
"data-keep-action": "keep-local",
|
|
3581
|
-
onClick: () => void view.resolve(conflict, "local"),
|
|
3582
|
-
disabled: view.busyId !== void 0,
|
|
3583
|
-
children: view.labels.keepLocal
|
|
3584
|
-
}
|
|
3585
|
-
),
|
|
3586
|
-
/* @__PURE__ */ jsx17(
|
|
3587
|
-
"button",
|
|
3588
|
-
{
|
|
3589
|
-
type: "button",
|
|
3590
|
-
"data-keep-action": "use-server",
|
|
3591
|
-
onClick: () => void view.resolve(conflict, "remote"),
|
|
3592
|
-
disabled: view.busyId !== void 0,
|
|
3593
|
-
children: view.labels.useServer
|
|
3594
|
-
}
|
|
3595
|
-
),
|
|
3596
|
-
/* @__PURE__ */ jsx17(
|
|
3597
|
-
"button",
|
|
3598
|
-
{
|
|
3599
|
-
type: "button",
|
|
3600
|
-
"data-keep-action": "manual-merge",
|
|
3601
|
-
onClick: () => void view.resolve(conflict, "manual"),
|
|
3602
|
-
disabled: view.busyId !== void 0 || !onManualMerge,
|
|
3603
|
-
children: view.labels.manualMerge
|
|
3604
|
-
}
|
|
3605
|
-
)
|
|
3606
|
-
] })
|
|
3607
|
-
] }, conflict.id))
|
|
3608
|
-
] }) : null,
|
|
3609
|
-
view.showBackupRecovery ? /* @__PURE__ */ jsxs11("section", { "data-recovery": "backup", children: [
|
|
3610
|
-
/* @__PURE__ */ jsx17("h3", { children: view.labels.backupRecovery }),
|
|
3611
|
-
/* @__PURE__ */ jsx17("p", { children: view.labels.backupRecoveryDescription }),
|
|
3612
|
-
backup ?? (showBackupControls ? /* @__PURE__ */ jsx17(KeepBackup, {}) : null)
|
|
3613
|
-
] }) : null,
|
|
3614
|
-
view.error ? /* @__PURE__ */ jsx17("p", { id: "keepkit-sync-recovery-error", role: "alert", "aria-live": "assertive", children: view.error instanceof Error ? view.error.message : view.labels.error }) : null
|
|
3615
|
-
]
|
|
3616
|
-
}
|
|
3617
|
-
);
|
|
3618
|
-
}
|
|
3619
|
-
function ConflictPreview({
|
|
3620
|
-
item,
|
|
3621
|
-
heading,
|
|
3622
|
-
updatedAtLabel,
|
|
3623
|
-
noteLabel,
|
|
3624
|
-
side
|
|
3625
|
-
}) {
|
|
3626
|
-
return /* @__PURE__ */ jsxs11("article", { "data-conflict-version": side, "aria-label": heading, children: [
|
|
3627
|
-
/* @__PURE__ */ jsx17("h4", { children: heading }),
|
|
3628
|
-
/* @__PURE__ */ jsxs11("dl", { children: [
|
|
3629
|
-
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3630
|
-
/* @__PURE__ */ jsx17("dt", { children: updatedAtLabel }),
|
|
3631
|
-
/* @__PURE__ */ jsx17("dd", { children: item ? /* @__PURE__ */ jsx17("time", { dateTime: new Date(item.updatedAt).toISOString(), children: formatConflictDate(item.updatedAt) }) : "\u2014" })
|
|
3632
|
-
] }),
|
|
3633
|
-
/* @__PURE__ */ jsxs11("div", { children: [
|
|
3634
|
-
/* @__PURE__ */ jsx17("dt", { children: noteLabel }),
|
|
3635
|
-
/* @__PURE__ */ jsx17("dd", { children: item?.note || "\u2014" })
|
|
3636
|
-
] })
|
|
3637
|
-
] })
|
|
3638
|
-
] });
|
|
3639
|
-
}
|
|
3640
|
-
function formatConflictDate(timestamp) {
|
|
3641
|
-
return new Date(timestamp).toISOString().slice(0, 10);
|
|
3741
|
+
// src/features/navigation/KeepShortcutHint.tsx
|
|
3742
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3743
|
+
function KeepShortcutHint({ shortcut, separator = "+", ...props }) {
|
|
3744
|
+
const keys = typeof shortcut === "string" ? shortcut.split("+") : [...shortcut];
|
|
3745
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
3746
|
+
const keyedKeys = keys.map((key) => {
|
|
3747
|
+
const occurrence = occurrences.get(key) ?? 0;
|
|
3748
|
+
occurrences.set(key, occurrence + 1);
|
|
3749
|
+
return { id: `${key}-${occurrence}`, value: key };
|
|
3750
|
+
});
|
|
3751
|
+
return /* @__PURE__ */ jsx19("span", { ...props, "data-keepkit": "shortcut-hint", "aria-hidden": props["aria-hidden"] ?? true, children: keyedKeys.map((key, index) => /* @__PURE__ */ jsxs14("span", { children: [
|
|
3752
|
+
index > 0 ? /* @__PURE__ */ jsx19("span", { "data-shortcut-separator": "true", children: separator }) : null,
|
|
3753
|
+
/* @__PURE__ */ jsx19("kbd", { children: key.value.trim() })
|
|
3754
|
+
] }, key.id)) });
|
|
3642
3755
|
}
|
|
3643
|
-
|
|
3644
|
-
// src/hooks/
|
|
3645
|
-
import {
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
const
|
|
3649
|
-
const
|
|
3650
|
-
const
|
|
3651
|
-
const
|
|
3652
|
-
const
|
|
3653
|
-
const
|
|
3654
|
-
|
|
3655
|
-
const
|
|
3756
|
+
|
|
3757
|
+
// src/features/editor/hooks/useKeepNoteEditor.ts
|
|
3758
|
+
import { useKeepItem as useKeepItem3 } from "@keepkit/core/react";
|
|
3759
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState10 } from "react";
|
|
3760
|
+
function useKeepNoteEditor({ item, debounceMs, onSaved, onSaveError }) {
|
|
3761
|
+
const itemState = useKeepItem3(item);
|
|
3762
|
+
const { error, isMutating, item: savedItem, updateNote } = itemState;
|
|
3763
|
+
const [note, setNote] = useState10(item.note ?? "");
|
|
3764
|
+
const baselineNote = savedItem?.note ?? item.note ?? "";
|
|
3765
|
+
const isDirty = note !== baselineNote;
|
|
3766
|
+
const lastSavedNoteRef = useRef5(void 0);
|
|
3767
|
+
useEffect7(() => setNote(baselineNote), [baselineNote]);
|
|
3768
|
+
const save = useCallback4(async () => {
|
|
3769
|
+
const nextNote = note.trim() || void 0;
|
|
3770
|
+
try {
|
|
3771
|
+
await updateNote(nextNote);
|
|
3772
|
+
lastSavedNoteRef.current = note;
|
|
3773
|
+
onSaved?.(nextNote);
|
|
3774
|
+
} catch (cause) {
|
|
3775
|
+
onSaveError?.(cause);
|
|
3776
|
+
throw cause;
|
|
3777
|
+
}
|
|
3778
|
+
}, [note, onSaveError, onSaved, updateNote]);
|
|
3779
|
+
useEffect7(() => {
|
|
3780
|
+
if (!isDirty || debounceMs <= 0 || lastSavedNoteRef.current === note) return;
|
|
3781
|
+
const timer = window.setTimeout(() => void save().catch(() => void 0), debounceMs);
|
|
3782
|
+
return () => window.clearTimeout(timer);
|
|
3783
|
+
}, [debounceMs, isDirty, note, save]);
|
|
3784
|
+
const state = {
|
|
3785
|
+
item,
|
|
3786
|
+
note,
|
|
3787
|
+
setNote,
|
|
3788
|
+
isDirty,
|
|
3789
|
+
isSaving: isMutating,
|
|
3790
|
+
error,
|
|
3791
|
+
save
|
|
3792
|
+
};
|
|
3793
|
+
const submit = (event) => {
|
|
3794
|
+
event.preventDefault();
|
|
3795
|
+
void save().catch(() => void 0);
|
|
3796
|
+
};
|
|
3656
3797
|
return {
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
return;
|
|
3669
|
-
}
|
|
3670
|
-
await context.flushSync();
|
|
3798
|
+
state,
|
|
3799
|
+
submit,
|
|
3800
|
+
handleKeyDown: (event) => {
|
|
3801
|
+
if (event.key !== "Enter" || !event.ctrlKey && !event.metaKey) return;
|
|
3802
|
+
event.preventDefault();
|
|
3803
|
+
void save().catch(() => void 0);
|
|
3804
|
+
},
|
|
3805
|
+
labels: {
|
|
3806
|
+
note: useUiLabel("note"),
|
|
3807
|
+
save: useUiLabel("saveNote"),
|
|
3808
|
+
error: useUiLabel("error")
|
|
3671
3809
|
}
|
|
3672
3810
|
};
|
|
3673
3811
|
}
|
|
3674
|
-
function getErrorMessage4(error) {
|
|
3675
|
-
return error instanceof Error ? error.message : "Sync failed.";
|
|
3676
|
-
}
|
|
3677
3812
|
|
|
3678
|
-
// src/
|
|
3679
|
-
import { jsx as
|
|
3680
|
-
function
|
|
3681
|
-
|
|
3682
|
-
|
|
3813
|
+
// src/features/editor/KeepNoteEditor.tsx
|
|
3814
|
+
import { Fragment as Fragment10, jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3815
|
+
function KeepNoteEditor({
|
|
3816
|
+
item,
|
|
3817
|
+
label,
|
|
3818
|
+
saveLabel,
|
|
3819
|
+
placeholder,
|
|
3820
|
+
debounceMs = 300,
|
|
3821
|
+
showShortcutHint = false,
|
|
3822
|
+
onSaved,
|
|
3823
|
+
onSaveError,
|
|
3824
|
+
render,
|
|
3683
3825
|
children,
|
|
3826
|
+
asChild = false,
|
|
3684
3827
|
className,
|
|
3685
|
-
...
|
|
3828
|
+
...formProps
|
|
3686
3829
|
}) {
|
|
3687
|
-
const view =
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3830
|
+
const view = useKeepNoteEditor({ item, debounceMs, onSaved, onSaveError });
|
|
3831
|
+
const { error, isDirty, isSaving, note, setNote } = view.state;
|
|
3832
|
+
const contentChildren = asChild && isValidElement7(children) ? void 0 : children;
|
|
3833
|
+
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? /* @__PURE__ */ jsxs15(Fragment10, { children: [
|
|
3834
|
+
/* @__PURE__ */ jsxs15("label", { children: [
|
|
3835
|
+
label ?? view.labels.note,
|
|
3836
|
+
/* @__PURE__ */ jsx20(
|
|
3837
|
+
"textarea",
|
|
3838
|
+
{
|
|
3839
|
+
"data-keep-action": "edit-note",
|
|
3840
|
+
value: note,
|
|
3841
|
+
onChange: (event) => setNote(event.currentTarget.value),
|
|
3842
|
+
placeholder,
|
|
3843
|
+
disabled: isSaving,
|
|
3844
|
+
onKeyDown: view.handleKeyDown
|
|
3845
|
+
}
|
|
3846
|
+
)
|
|
3847
|
+
] }),
|
|
3848
|
+
/* @__PURE__ */ jsxs15("button", { type: "submit", "data-keep-action": "save-note", disabled: isSaving, "aria-busy": isSaving, children: [
|
|
3849
|
+
saveLabel ?? view.labels.save,
|
|
3850
|
+
showShortcutHint ? /* @__PURE__ */ jsx20(KeepShortcutHint, { shortcut: "Ctrl+Enter" }) : null
|
|
3851
|
+
] })
|
|
3852
|
+
] });
|
|
3853
|
+
if (!asChild) {
|
|
3854
|
+
return /* @__PURE__ */ jsxs15(
|
|
3855
|
+
"form",
|
|
3856
|
+
{
|
|
3857
|
+
...formProps,
|
|
3858
|
+
className,
|
|
3859
|
+
"data-keepkit": "note-editor",
|
|
3860
|
+
onSubmit: view.submit,
|
|
3861
|
+
"aria-busy": isSaving || formProps["aria-busy"],
|
|
3862
|
+
"data-state": error ? "error" : isDirty ? "dirty" : "clean",
|
|
3863
|
+
"data-loading": isSaving ? "true" : void 0,
|
|
3864
|
+
"data-disabled": isSaving ? "true" : void 0,
|
|
3865
|
+
children: [
|
|
3866
|
+
body,
|
|
3867
|
+
error ? /* @__PURE__ */ jsx20("p", { role: "alert", children: getErrorMessage3(error, view.labels.error) }) : null
|
|
3868
|
+
]
|
|
3869
|
+
}
|
|
3870
|
+
);
|
|
3871
|
+
}
|
|
3872
|
+
return renderRoot(
|
|
3873
|
+
true,
|
|
3874
|
+
isValidElement7(children) ? children : void 0,
|
|
3691
3875
|
{
|
|
3692
|
-
...
|
|
3876
|
+
...formProps,
|
|
3693
3877
|
className,
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
"
|
|
3697
|
-
"data-state":
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
type: "button",
|
|
3704
|
-
"data-keep-action": "retry-sync",
|
|
3705
|
-
onClick: () => void view.retry(),
|
|
3706
|
-
disabled: view.isMutating,
|
|
3707
|
-
children: view.retryLabel
|
|
3708
|
-
}
|
|
3709
|
-
) : null,
|
|
3710
|
-
view.hasConflicts ? /* @__PURE__ */ jsx18(
|
|
3711
|
-
"button",
|
|
3712
|
-
{
|
|
3713
|
-
type: "button",
|
|
3714
|
-
"data-keep-action": "resolve-conflicts",
|
|
3715
|
-
onClick: onResolveConflicts,
|
|
3716
|
-
disabled: !onResolveConflicts,
|
|
3717
|
-
children: view.resolveLabel
|
|
3718
|
-
}
|
|
3719
|
-
) : null
|
|
3720
|
-
]
|
|
3721
|
-
}
|
|
3878
|
+
"data-keepkit": "note-editor",
|
|
3879
|
+
onSubmit: view.submit,
|
|
3880
|
+
"aria-busy": isSaving || formProps["aria-busy"],
|
|
3881
|
+
"data-state": error ? "error" : isDirty ? "dirty" : "clean",
|
|
3882
|
+
"data-loading": isSaving ? "true" : void 0,
|
|
3883
|
+
"data-disabled": isSaving ? "true" : void 0
|
|
3884
|
+
},
|
|
3885
|
+
body,
|
|
3886
|
+
"KeepNoteEditor"
|
|
3722
3887
|
);
|
|
3723
3888
|
}
|
|
3889
|
+
function getErrorMessage3(error, fallback) {
|
|
3890
|
+
return error instanceof Error ? error.message : fallback;
|
|
3891
|
+
}
|
|
3724
3892
|
|
|
3725
|
-
// src/hooks/useKeepTagEditor.ts
|
|
3893
|
+
// src/features/editor/hooks/useKeepTagEditor.ts
|
|
3726
3894
|
import { useKeepItem as useKeepItem4 } from "@keepkit/core/react";
|
|
3727
|
-
import { useCallback as
|
|
3895
|
+
import { useCallback as useCallback5, useEffect as useEffect8, useState as useState11 } from "react";
|
|
3728
3896
|
function useKeepTagEditor({ item, onSaved, onSaveError }) {
|
|
3729
3897
|
const itemState = useKeepItem4(item);
|
|
3730
3898
|
const [tags, setTags] = useState11(item.tags ?? []);
|
|
3731
3899
|
const [input, setInput] = useState11("");
|
|
3732
|
-
|
|
3733
|
-
const save =
|
|
3900
|
+
useEffect8(() => setTags(itemState.item?.tags ?? item.tags ?? []), [item.tags, itemState.item?.tags]);
|
|
3901
|
+
const save = useCallback5(async () => {
|
|
3734
3902
|
const nextTags = normalizeUiTags(tags);
|
|
3735
3903
|
try {
|
|
3736
3904
|
await itemState.updateTags(nextTags);
|
|
@@ -3775,8 +3943,8 @@ function useKeepTagEditor({ item, onSaved, onSaveError }) {
|
|
|
3775
3943
|
};
|
|
3776
3944
|
}
|
|
3777
3945
|
|
|
3778
|
-
// src/KeepTagEditor.tsx
|
|
3779
|
-
import { Fragment as
|
|
3946
|
+
// src/features/editor/KeepTagEditor.tsx
|
|
3947
|
+
import { Fragment as Fragment11, jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3780
3948
|
function KeepTagEditor({
|
|
3781
3949
|
item,
|
|
3782
3950
|
availableTags = [],
|
|
@@ -3787,10 +3955,10 @@ function KeepTagEditor({
|
|
|
3787
3955
|
}) {
|
|
3788
3956
|
const view = useKeepTagEditor({ item, onSaved, onSaveError });
|
|
3789
3957
|
const { isSaving, tags } = view.state;
|
|
3790
|
-
const body = render ? render(view.state) : /* @__PURE__ */
|
|
3791
|
-
/* @__PURE__ */
|
|
3958
|
+
const body = render ? render(view.state) : /* @__PURE__ */ jsxs16(Fragment11, { children: [
|
|
3959
|
+
/* @__PURE__ */ jsxs16("label", { children: [
|
|
3792
3960
|
view.labels.tags,
|
|
3793
|
-
/* @__PURE__ */
|
|
3961
|
+
/* @__PURE__ */ jsx21(
|
|
3794
3962
|
"input",
|
|
3795
3963
|
{
|
|
3796
3964
|
"data-keep-action": "edit-tags",
|
|
@@ -3801,14 +3969,14 @@ function KeepTagEditor({
|
|
|
3801
3969
|
}
|
|
3802
3970
|
)
|
|
3803
3971
|
] }),
|
|
3804
|
-
availableTags.length > 0 ? /* @__PURE__ */
|
|
3805
|
-
/* @__PURE__ */
|
|
3972
|
+
availableTags.length > 0 ? /* @__PURE__ */ jsx21("datalist", { id: `keep-tags-${item.id}`, children: availableTags.map((tag) => /* @__PURE__ */ jsx21("option", { value: tag }, tag)) }) : null,
|
|
3973
|
+
/* @__PURE__ */ jsx21("ul", { "aria-label": view.labels.tags, children: tags.map((tag) => /* @__PURE__ */ jsxs16("li", { children: [
|
|
3806
3974
|
tag,
|
|
3807
|
-
/* @__PURE__ */
|
|
3975
|
+
/* @__PURE__ */ jsx21("button", { type: "button", "data-keep-action": "remove-tag", onClick: () => view.removeTag(tag), children: view.labels.remove })
|
|
3808
3976
|
] }, tag)) }),
|
|
3809
|
-
/* @__PURE__ */
|
|
3977
|
+
/* @__PURE__ */ jsx21("button", { type: "submit", "data-keep-action": "apply-tags", disabled: isSaving, "aria-busy": isSaving, children: view.labels.apply })
|
|
3810
3978
|
] });
|
|
3811
|
-
return /* @__PURE__ */
|
|
3979
|
+
return /* @__PURE__ */ jsxs16(
|
|
3812
3980
|
"form",
|
|
3813
3981
|
{
|
|
3814
3982
|
...props,
|
|
@@ -3820,20 +3988,41 @@ function KeepTagEditor({
|
|
|
3820
3988
|
"data-disabled": isSaving ? "true" : void 0,
|
|
3821
3989
|
children: [
|
|
3822
3990
|
body,
|
|
3823
|
-
view.error ? /* @__PURE__ */
|
|
3991
|
+
view.error ? /* @__PURE__ */ jsx21("p", { role: "alert", children: getErrorMessage4(view.error, view.labels.error) }) : null
|
|
3824
3992
|
]
|
|
3825
3993
|
}
|
|
3826
3994
|
);
|
|
3827
3995
|
}
|
|
3828
|
-
function
|
|
3996
|
+
function getErrorMessage4(error, fallback) {
|
|
3829
3997
|
return error instanceof Error ? error.message : fallback;
|
|
3830
3998
|
}
|
|
3831
3999
|
|
|
3832
|
-
// src/
|
|
4000
|
+
// src/features/feedback/useKeepToastFeedback.ts
|
|
4001
|
+
import { useCallback as useCallback6 } from "react";
|
|
4002
|
+
function useKeepToastFeedback(showToast) {
|
|
4003
|
+
return useCallback6(
|
|
4004
|
+
(event) => {
|
|
4005
|
+
if (!("undo" in event)) {
|
|
4006
|
+
showToast(event.message);
|
|
4007
|
+
return;
|
|
4008
|
+
}
|
|
4009
|
+
showToast(event.message, {
|
|
4010
|
+
action: {
|
|
4011
|
+
label: event.undoLabel,
|
|
4012
|
+
onClick: () => void event.undo()
|
|
4013
|
+
}
|
|
4014
|
+
});
|
|
4015
|
+
},
|
|
4016
|
+
[showToast]
|
|
4017
|
+
);
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
// src/features/navigation/KeepTourBar.tsx
|
|
3833
4021
|
import { useKeepNavigator } from "@keepkit/core/react";
|
|
4022
|
+
import { useId } from "react";
|
|
3834
4023
|
|
|
3835
|
-
// src/hooks/useKeepTourShortcuts.ts
|
|
3836
|
-
import { useEffect as
|
|
4024
|
+
// src/features/navigation/hooks/useKeepTourShortcuts.ts
|
|
4025
|
+
import { useEffect as useEffect9 } from "react";
|
|
3837
4026
|
function useKeepTourShortcuts({
|
|
3838
4027
|
onNext,
|
|
3839
4028
|
onPrev,
|
|
@@ -3844,7 +4033,7 @@ function useKeepTourShortcuts({
|
|
|
3844
4033
|
prevKeys = ["k", "["],
|
|
3845
4034
|
onError
|
|
3846
4035
|
}) {
|
|
3847
|
-
|
|
4036
|
+
useEffect9(() => {
|
|
3848
4037
|
if (!enabled) return;
|
|
3849
4038
|
const handleKeyDown = (event) => {
|
|
3850
4039
|
if (!allowInEditable && isEditableTarget(event.target)) return;
|
|
@@ -3864,8 +4053,8 @@ function isEditableTarget(target) {
|
|
|
3864
4053
|
return target.isContentEditable || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT";
|
|
3865
4054
|
}
|
|
3866
4055
|
|
|
3867
|
-
// src/KeepTourBar.tsx
|
|
3868
|
-
import { jsx as
|
|
4056
|
+
// src/features/navigation/KeepTourBar.tsx
|
|
4057
|
+
import { Fragment as Fragment12, jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3869
4058
|
function KeepTourBar({
|
|
3870
4059
|
navigation: providedNavigation,
|
|
3871
4060
|
currentId,
|
|
@@ -3881,8 +4070,12 @@ function KeepTourBar({
|
|
|
3881
4070
|
nextLabel,
|
|
3882
4071
|
backLabel,
|
|
3883
4072
|
keyboardShortcuts = false,
|
|
4073
|
+
showShortcutHint = false,
|
|
3884
4074
|
shortcutOptions,
|
|
3885
4075
|
progress,
|
|
4076
|
+
getItemTitle = (item) => getMetaTitle(item.meta) ?? item.id,
|
|
4077
|
+
children,
|
|
4078
|
+
asChild = false,
|
|
3886
4079
|
...props
|
|
3887
4080
|
}) {
|
|
3888
4081
|
const ownNavigation = useKeepNavigator({ currentId, initialIndex });
|
|
@@ -3903,36 +4096,82 @@ function KeepTourBar({
|
|
|
3903
4096
|
return resolvedPrev();
|
|
3904
4097
|
}
|
|
3905
4098
|
});
|
|
3906
|
-
|
|
3907
|
-
showProgress ? /* @__PURE__ */
|
|
3908
|
-
/* @__PURE__ */
|
|
4099
|
+
const body = /* @__PURE__ */ jsxs17(Fragment12, { children: [
|
|
4100
|
+
showProgress ? /* @__PURE__ */ jsx22("span", { "data-keepkit": "tour-progress", "aria-live": "polite", children: progress ?? `${navigation.currentPosition ?? 0} / ${navigation.items.length}` }) : null,
|
|
4101
|
+
/* @__PURE__ */ jsx22(
|
|
3909
4102
|
TourAction,
|
|
3910
4103
|
{
|
|
3911
4104
|
href: prevHref,
|
|
3912
4105
|
disabled: !navigation.hasPrev,
|
|
3913
4106
|
onClick: prevHref ? onPrev : resolvedPrev,
|
|
3914
4107
|
"data-keep-action": "tour-prev",
|
|
4108
|
+
shortcut: showShortcutHint ? shortcutOptions?.prevKeys?.[0] ?? "K" : void 0,
|
|
4109
|
+
preview: navigation.prevItem ? /* @__PURE__ */ jsxs17(Fragment12, { children: [
|
|
4110
|
+
previousLabel,
|
|
4111
|
+
": ",
|
|
4112
|
+
getItemTitle(navigation.prevItem)
|
|
4113
|
+
] }) : void 0,
|
|
3915
4114
|
children: previousLabel
|
|
3916
4115
|
}
|
|
3917
4116
|
),
|
|
3918
|
-
/* @__PURE__ */
|
|
4117
|
+
/* @__PURE__ */ jsx22(
|
|
3919
4118
|
TourAction,
|
|
3920
4119
|
{
|
|
3921
4120
|
href: nextHref,
|
|
3922
4121
|
disabled: !navigation.hasNext,
|
|
3923
4122
|
onClick: nextHref ? onNext : resolvedNext,
|
|
3924
4123
|
"data-keep-action": "tour-next",
|
|
4124
|
+
shortcut: showShortcutHint ? shortcutOptions?.nextKeys?.[0] ?? "J" : void 0,
|
|
4125
|
+
preview: navigation.nextItem ? /* @__PURE__ */ jsxs17(Fragment12, { children: [
|
|
4126
|
+
nextItemLabel,
|
|
4127
|
+
": ",
|
|
4128
|
+
getItemTitle(navigation.nextItem)
|
|
4129
|
+
] }) : void 0,
|
|
3925
4130
|
children: nextItemLabel
|
|
3926
4131
|
}
|
|
3927
4132
|
),
|
|
3928
|
-
backHref || onBack ? /* @__PURE__ */
|
|
4133
|
+
backHref || onBack ? /* @__PURE__ */ jsx22(TourAction, { href: backHref, onClick: onBack, "data-keep-action": "tour-back", children: listLabel }) : null
|
|
3929
4134
|
] });
|
|
4135
|
+
return renderRoot(
|
|
4136
|
+
asChild,
|
|
4137
|
+
children,
|
|
4138
|
+
{ ...props, "data-keepkit": "tour-bar", "aria-label": props["aria-label"] ?? labels.pagination },
|
|
4139
|
+
body,
|
|
4140
|
+
"KeepTourBar"
|
|
4141
|
+
);
|
|
3930
4142
|
}
|
|
3931
|
-
function TourAction({ href, disabled = false, onClick, children, ...props }) {
|
|
4143
|
+
function TourAction({ href, disabled = false, onClick, children, preview, shortcut, ...props }) {
|
|
4144
|
+
const previewId = useId();
|
|
4145
|
+
const content = /* @__PURE__ */ jsxs17(Fragment12, { children: [
|
|
4146
|
+
/* @__PURE__ */ jsx22("span", { "data-tour-label": "true", children }),
|
|
4147
|
+
shortcut ? /* @__PURE__ */ jsx22(KeepShortcutHint, { shortcut }) : null,
|
|
4148
|
+
preview ? /* @__PURE__ */ jsx22("small", { id: previewId, "data-tour-preview": "true", children: preview }) : null
|
|
4149
|
+
] });
|
|
3932
4150
|
if (href && !disabled) {
|
|
3933
|
-
return /* @__PURE__ */
|
|
4151
|
+
return /* @__PURE__ */ jsx22(
|
|
4152
|
+
"a",
|
|
4153
|
+
{
|
|
4154
|
+
...props,
|
|
4155
|
+
href,
|
|
4156
|
+
onClick: () => void onClick?.(),
|
|
4157
|
+
"aria-label": String(children),
|
|
4158
|
+
"aria-describedby": preview ? previewId : void 0,
|
|
4159
|
+
children: content
|
|
4160
|
+
}
|
|
4161
|
+
);
|
|
3934
4162
|
}
|
|
3935
|
-
return /* @__PURE__ */
|
|
4163
|
+
return /* @__PURE__ */ jsx22(
|
|
4164
|
+
"button",
|
|
4165
|
+
{
|
|
4166
|
+
...props,
|
|
4167
|
+
type: "button",
|
|
4168
|
+
disabled,
|
|
4169
|
+
onClick: () => void onClick?.(),
|
|
4170
|
+
"aria-label": String(children),
|
|
4171
|
+
"aria-describedby": preview ? previewId : void 0,
|
|
4172
|
+
children: content
|
|
4173
|
+
}
|
|
4174
|
+
);
|
|
3936
4175
|
}
|
|
3937
4176
|
var KeepNavigator = KeepTourBar;
|
|
3938
4177
|
function navigateTo(item, href) {
|
|
@@ -3940,48 +4179,14 @@ function navigateTo(item, href) {
|
|
|
3940
4179
|
window.location.assign(href);
|
|
3941
4180
|
}
|
|
3942
4181
|
|
|
3943
|
-
// src/
|
|
3944
|
-
import {
|
|
3945
|
-
function useKeepUndo() {
|
|
3946
|
-
const context = useKeepContext6();
|
|
3947
|
-
const emitFeedback = useKeepUiFeedback();
|
|
3948
|
-
const restoredMessage = useUiLabel("restoredMessage");
|
|
3949
|
-
return {
|
|
3950
|
-
canUndo: context.undo.canUndo,
|
|
3951
|
-
undo: async () => {
|
|
3952
|
-
const items = context.lastChange?.items ?? (context.lastChange?.item ? [context.lastChange.item] : []);
|
|
3953
|
-
await context.undoLastRemoval();
|
|
3954
|
-
if (items.length > 0) {
|
|
3955
|
-
emitFeedback({ type: "item-restored", item: items[0], items, message: restoredMessage });
|
|
3956
|
-
}
|
|
3957
|
-
},
|
|
3958
|
-
message: useUiLabel("undoAvailable"),
|
|
3959
|
-
label: useUiLabel("undo")
|
|
3960
|
-
};
|
|
3961
|
-
}
|
|
3962
|
-
|
|
3963
|
-
// src/KeepUndo.tsx
|
|
3964
|
-
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3965
|
-
function KeepUndo({ children, label, ...props }) {
|
|
3966
|
-
const view = useKeepUndo();
|
|
3967
|
-
if (!view.canUndo) return null;
|
|
3968
|
-
return /* @__PURE__ */ jsxs15("div", { ...props, role: "status", "aria-live": "polite", "data-keepkit": "undo", "data-state": "available", children: [
|
|
3969
|
-
children ?? view.message,
|
|
3970
|
-
/* @__PURE__ */ jsx21("button", { type: "button", "data-keep-action": "undo", onClick: () => void view.undo(), children: label ?? view.label })
|
|
3971
|
-
] });
|
|
3972
|
-
}
|
|
3973
|
-
|
|
3974
|
-
// src/status.tsx
|
|
3975
|
-
import { isValidElement as isValidElement6 } from "react";
|
|
4182
|
+
// src/features/status/status.tsx
|
|
4183
|
+
import { isValidElement as isValidElement8 } from "react";
|
|
3976
4184
|
|
|
3977
|
-
// src/hooks/useStatusViews.ts
|
|
3978
|
-
import { useKeepContext as
|
|
3979
|
-
import { useEffect as
|
|
3980
|
-
function useKeepEmptyState() {
|
|
3981
|
-
return useUiLabel("noItems").replace(/\.$/, "");
|
|
3982
|
-
}
|
|
4185
|
+
// src/features/status/hooks/useStatusViews.ts
|
|
4186
|
+
import { useKeepContext as useKeepContext4 } from "@keepkit/core/react";
|
|
4187
|
+
import { useEffect as useEffect10, useRef as useRef6, useState as useState12 } from "react";
|
|
3983
4188
|
function useKeepStatus(status) {
|
|
3984
|
-
const context =
|
|
4189
|
+
const context = useKeepContext4();
|
|
3985
4190
|
const resolvedStatus = status ?? getDerivedStatus(context);
|
|
3986
4191
|
const state = {
|
|
3987
4192
|
status: resolvedStatus,
|
|
@@ -3992,13 +4197,13 @@ function useKeepStatus(status) {
|
|
|
3992
4197
|
return { state, defaultLabel: useUiLabel(getStatusLabelKey3(resolvedStatus)) };
|
|
3993
4198
|
}
|
|
3994
4199
|
function useKeepAnnouncements(messages) {
|
|
3995
|
-
const context =
|
|
4200
|
+
const context = useKeepContext4();
|
|
3996
4201
|
const savedMessage = useUiLabel("savedMessage", messages?.save);
|
|
3997
4202
|
const removedMessage = useUiLabel("removedMessage", messages?.remove);
|
|
3998
4203
|
const noteSavedMessage = useUiLabel("noteSavedMessage", messages?.note);
|
|
3999
4204
|
const [message, setMessage] = useState12("");
|
|
4000
|
-
const lastChangeRef =
|
|
4001
|
-
|
|
4205
|
+
const lastChangeRef = useRef6(void 0);
|
|
4206
|
+
useEffect10(() => {
|
|
4002
4207
|
const change = context.lastChange;
|
|
4003
4208
|
if (!change || change === lastChangeRef.current) return;
|
|
4004
4209
|
lastChangeRef.current = change;
|
|
@@ -4014,43 +4219,19 @@ function getDerivedStatus(context) {
|
|
|
4014
4219
|
if (context.isMutating) return "saving";
|
|
4015
4220
|
if (context.isLoading && !context.isHydrated) return "loading";
|
|
4016
4221
|
if (context.isHydrated && context.items.length === 0) return "empty";
|
|
4017
|
-
return "idle";
|
|
4018
|
-
}
|
|
4019
|
-
function getStatusLabelKey3(status) {
|
|
4020
|
-
if (status === "empty") return "noItems";
|
|
4021
|
-
if (status === "loading") return "loadingItems";
|
|
4022
|
-
if (status === "error") return "error";
|
|
4023
|
-
if (status === "saving") return "saving";
|
|
4024
|
-
if (status === "syncing") return "syncing";
|
|
4025
|
-
return "saved";
|
|
4026
|
-
}
|
|
4027
|
-
|
|
4028
|
-
// src/status.tsx
|
|
4029
|
-
import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4030
|
-
function KeepEmptyState({
|
|
4031
|
-
title,
|
|
4032
|
-
description,
|
|
4033
|
-
action,
|
|
4034
|
-
children,
|
|
4035
|
-
asChild = false,
|
|
4036
|
-
className,
|
|
4037
|
-
...rootProps
|
|
4038
|
-
}) {
|
|
4039
|
-
const defaultTitle = useKeepEmptyState();
|
|
4040
|
-
const contentChildren = asChild && isValidElement6(children) ? void 0 : children;
|
|
4041
|
-
const body = contentChildren ?? /* @__PURE__ */ jsxs16(Fragment9, { children: [
|
|
4042
|
-
/* @__PURE__ */ jsx22("h2", { children: title ?? defaultTitle }),
|
|
4043
|
-
description ? /* @__PURE__ */ jsx22("p", { children: description }) : null,
|
|
4044
|
-
action
|
|
4045
|
-
] });
|
|
4046
|
-
return renderRoot(
|
|
4047
|
-
asChild,
|
|
4048
|
-
children,
|
|
4049
|
-
{ ...rootProps, className, "data-keepkit": "empty-state", "data-state": "empty" },
|
|
4050
|
-
body,
|
|
4051
|
-
"KeepEmptyState"
|
|
4052
|
-
);
|
|
4222
|
+
return "idle";
|
|
4223
|
+
}
|
|
4224
|
+
function getStatusLabelKey3(status) {
|
|
4225
|
+
if (status === "empty") return "noItems";
|
|
4226
|
+
if (status === "loading") return "loadingItems";
|
|
4227
|
+
if (status === "error") return "error";
|
|
4228
|
+
if (status === "saving") return "saving";
|
|
4229
|
+
if (status === "syncing") return "syncing";
|
|
4230
|
+
return "saved";
|
|
4053
4231
|
}
|
|
4232
|
+
|
|
4233
|
+
// src/features/status/status.tsx
|
|
4234
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
4054
4235
|
function KeepStatus({
|
|
4055
4236
|
status,
|
|
4056
4237
|
labels,
|
|
@@ -4061,12 +4242,12 @@ function KeepStatus({
|
|
|
4061
4242
|
...rootProps
|
|
4062
4243
|
}) {
|
|
4063
4244
|
const view = useKeepStatus(status);
|
|
4064
|
-
const contentChildren = asChild &&
|
|
4245
|
+
const contentChildren = asChild && isValidElement8(children) ? void 0 : children;
|
|
4065
4246
|
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? labels?.[view.state.status] ?? view.defaultLabel;
|
|
4066
4247
|
const role = rootProps.role ?? (view.state.status === "error" ? "alert" : "status");
|
|
4067
4248
|
return renderRoot(
|
|
4068
4249
|
asChild,
|
|
4069
|
-
|
|
4250
|
+
isValidElement8(children) ? children : void 0,
|
|
4070
4251
|
{
|
|
4071
4252
|
...rootProps,
|
|
4072
4253
|
className,
|
|
@@ -4082,7 +4263,7 @@ function KeepStatus({
|
|
|
4082
4263
|
}
|
|
4083
4264
|
function KeepAnnouncements({ messages, ...props }) {
|
|
4084
4265
|
const message = useKeepAnnouncements(messages);
|
|
4085
|
-
return /* @__PURE__ */
|
|
4266
|
+
return /* @__PURE__ */ jsx23(
|
|
4086
4267
|
"div",
|
|
4087
4268
|
{
|
|
4088
4269
|
...props,
|
|
@@ -4097,9 +4278,303 @@ function KeepAnnouncements({ messages, ...props }) {
|
|
|
4097
4278
|
}
|
|
4098
4279
|
var KeepAnnouncer = KeepAnnouncements;
|
|
4099
4280
|
|
|
4100
|
-
// src/
|
|
4101
|
-
import {
|
|
4102
|
-
import {
|
|
4281
|
+
// src/features/sync/hooks/useKeepSyncFeedback.ts
|
|
4282
|
+
import { useKeepContext as useKeepContext5 } from "@keepkit/core/react";
|
|
4283
|
+
import { useEffect as useEffect11, useRef as useRef7 } from "react";
|
|
4284
|
+
function useKeepSyncFeedback() {
|
|
4285
|
+
const { syncState } = useKeepContext5();
|
|
4286
|
+
const emitFeedback = useKeepUiFeedback();
|
|
4287
|
+
const completedMessage = useUiLabel("syncSynced");
|
|
4288
|
+
const failedMessage = useUiLabel("syncFailedMessage");
|
|
4289
|
+
const previousStatus = useRef7("idle");
|
|
4290
|
+
useEffect11(() => {
|
|
4291
|
+
const previous = previousStatus.current;
|
|
4292
|
+
previousStatus.current = syncState.status;
|
|
4293
|
+
if (syncState.status === "error" && previous !== "error") {
|
|
4294
|
+
emitFeedback({ type: "sync-failed", error: syncState.error, message: failedMessage });
|
|
4295
|
+
return;
|
|
4296
|
+
}
|
|
4297
|
+
if (syncState.status === "synced" && (previous === "pending" || previous === "syncing" || previous === "conflict" || previous === "error")) {
|
|
4298
|
+
emitFeedback({ type: "sync-completed", message: completedMessage });
|
|
4299
|
+
}
|
|
4300
|
+
}, [completedMessage, emitFeedback, failedMessage, syncState.error, syncState.status]);
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
// src/features/sync/KeepSyncFeedbackObserver.tsx
|
|
4304
|
+
function KeepSyncFeedbackObserver() {
|
|
4305
|
+
useKeepSyncFeedback();
|
|
4306
|
+
return null;
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
// src/features/sync/hooks/useKeepSyncRecoveryDialog.ts
|
|
4310
|
+
import { useKeepContext as useKeepContext6 } from "@keepkit/core/react";
|
|
4311
|
+
import { useEffect as useEffect12, useState as useState13 } from "react";
|
|
4312
|
+
function useKeepSyncRecoveryDialog(options) {
|
|
4313
|
+
const { open, onOpenChange, conflicts, onManualMerge } = options;
|
|
4314
|
+
const context = useKeepContext6();
|
|
4315
|
+
const conflictList = conflicts ?? context.syncState.conflicts ?? [];
|
|
4316
|
+
const hasRecovery = conflictList.length > 0 || context.syncState.status === "error" || Boolean(context.error);
|
|
4317
|
+
const [dismissed, setDismissed] = useState13(false);
|
|
4318
|
+
const [busyId, setBusyId] = useState13();
|
|
4319
|
+
const [error, setError] = useState13();
|
|
4320
|
+
useEffect12(() => {
|
|
4321
|
+
if (hasRecovery) setDismissed(false);
|
|
4322
|
+
}, [hasRecovery]);
|
|
4323
|
+
return {
|
|
4324
|
+
conflictList,
|
|
4325
|
+
isOpen: open ?? (hasRecovery && !dismissed),
|
|
4326
|
+
busyId,
|
|
4327
|
+
error,
|
|
4328
|
+
showBackupRecovery: context.syncState.status === "error" || Boolean(context.error),
|
|
4329
|
+
close: () => {
|
|
4330
|
+
setDismissed(true);
|
|
4331
|
+
onOpenChange?.(false);
|
|
4332
|
+
},
|
|
4333
|
+
resolve: async (conflict, resolution) => {
|
|
4334
|
+
setError(void 0);
|
|
4335
|
+
setBusyId(conflict.id);
|
|
4336
|
+
try {
|
|
4337
|
+
const merged = resolution === "manual" ? await onManualMerge?.(conflict) : void 0;
|
|
4338
|
+
if (resolution === "manual" && !merged) throw new Error("A manual merge result is required.");
|
|
4339
|
+
await context.resolveSyncConflict(conflict.id, resolution, merged);
|
|
4340
|
+
} catch (cause) {
|
|
4341
|
+
setError(cause);
|
|
4342
|
+
} finally {
|
|
4343
|
+
setBusyId(void 0);
|
|
4344
|
+
}
|
|
4345
|
+
},
|
|
4346
|
+
labels: {
|
|
4347
|
+
close: useUiLabel("close"),
|
|
4348
|
+
title: useUiLabel("resolveSync"),
|
|
4349
|
+
conflict: useUiLabel("syncConflict"),
|
|
4350
|
+
keepLocal: useUiLabel("keepLocal"),
|
|
4351
|
+
useServer: useUiLabel("useServer"),
|
|
4352
|
+
manualMerge: useUiLabel("manualMerge"),
|
|
4353
|
+
localVersion: useUiLabel("localVersion"),
|
|
4354
|
+
remoteVersion: useUiLabel("remoteVersion"),
|
|
4355
|
+
updatedAt: useUiLabel("updatedAt"),
|
|
4356
|
+
note: useUiLabel("note"),
|
|
4357
|
+
backupRecovery: useUiLabel("backupRecovery"),
|
|
4358
|
+
backupRecoveryDescription: useUiLabel("backupRecoveryDescription"),
|
|
4359
|
+
error: useUiLabel("error")
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4364
|
+
// src/features/sync/KeepSyncRecoveryDialog.tsx
|
|
4365
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
4366
|
+
function KeepSyncRecoveryDialog({
|
|
4367
|
+
open,
|
|
4368
|
+
onOpenChange,
|
|
4369
|
+
conflicts,
|
|
4370
|
+
onManualMerge,
|
|
4371
|
+
backup,
|
|
4372
|
+
showBackupControls = true,
|
|
4373
|
+
title,
|
|
4374
|
+
children,
|
|
4375
|
+
className,
|
|
4376
|
+
...props
|
|
4377
|
+
}) {
|
|
4378
|
+
const view = useKeepSyncRecoveryDialog({ open, onOpenChange, conflicts, onManualMerge });
|
|
4379
|
+
if (!view.isOpen) return null;
|
|
4380
|
+
return /* @__PURE__ */ jsxs18(
|
|
4381
|
+
"section",
|
|
4382
|
+
{
|
|
4383
|
+
...props,
|
|
4384
|
+
className,
|
|
4385
|
+
role: "dialog",
|
|
4386
|
+
"aria-modal": "true",
|
|
4387
|
+
"aria-labelledby": "keepkit-sync-recovery-title",
|
|
4388
|
+
"aria-describedby": view.error ? "keepkit-sync-recovery-error" : void 0,
|
|
4389
|
+
"aria-busy": view.busyId !== void 0,
|
|
4390
|
+
"data-keepkit": "sync-recovery",
|
|
4391
|
+
"data-state": view.conflictList.length > 0 ? "conflict" : "error",
|
|
4392
|
+
"data-loading": view.busyId !== void 0 ? "true" : void 0,
|
|
4393
|
+
children: [
|
|
4394
|
+
/* @__PURE__ */ jsxs18("header", { children: [
|
|
4395
|
+
/* @__PURE__ */ jsx24("h2", { id: "keepkit-sync-recovery-title", children: title ?? view.labels.title }),
|
|
4396
|
+
/* @__PURE__ */ jsx24("button", { type: "button", "data-keep-action": "close-dialog", onClick: view.close, "aria-label": view.labels.close, children: view.labels.close })
|
|
4397
|
+
] }),
|
|
4398
|
+
children,
|
|
4399
|
+
view.conflictList.length > 0 ? /* @__PURE__ */ jsxs18("div", { children: [
|
|
4400
|
+
/* @__PURE__ */ jsx24("p", { children: view.labels.conflict }),
|
|
4401
|
+
view.conflictList.map((conflict) => /* @__PURE__ */ jsxs18("article", { "data-conflict-id": conflict.id, children: [
|
|
4402
|
+
/* @__PURE__ */ jsx24("h3", { children: getMetaTitle(conflict.operation.item?.meta) ?? conflict.id }),
|
|
4403
|
+
/* @__PURE__ */ jsxs18("div", { "data-conflict-preview": true, children: [
|
|
4404
|
+
/* @__PURE__ */ jsx24(
|
|
4405
|
+
ConflictPreview,
|
|
4406
|
+
{
|
|
4407
|
+
item: conflict.operation.item,
|
|
4408
|
+
heading: view.labels.localVersion,
|
|
4409
|
+
updatedAtLabel: view.labels.updatedAt,
|
|
4410
|
+
noteLabel: view.labels.note,
|
|
4411
|
+
side: "local"
|
|
4412
|
+
}
|
|
4413
|
+
),
|
|
4414
|
+
/* @__PURE__ */ jsx24(
|
|
4415
|
+
ConflictPreview,
|
|
4416
|
+
{
|
|
4417
|
+
item: conflict.remote,
|
|
4418
|
+
heading: view.labels.remoteVersion,
|
|
4419
|
+
updatedAtLabel: view.labels.updatedAt,
|
|
4420
|
+
noteLabel: view.labels.note,
|
|
4421
|
+
side: "remote"
|
|
4422
|
+
}
|
|
4423
|
+
)
|
|
4424
|
+
] }),
|
|
4425
|
+
/* @__PURE__ */ jsxs18("div", { children: [
|
|
4426
|
+
/* @__PURE__ */ jsx24(
|
|
4427
|
+
"button",
|
|
4428
|
+
{
|
|
4429
|
+
type: "button",
|
|
4430
|
+
"data-keep-action": "keep-local",
|
|
4431
|
+
onClick: () => void view.resolve(conflict, "local"),
|
|
4432
|
+
disabled: view.busyId !== void 0,
|
|
4433
|
+
children: view.labels.keepLocal
|
|
4434
|
+
}
|
|
4435
|
+
),
|
|
4436
|
+
/* @__PURE__ */ jsx24(
|
|
4437
|
+
"button",
|
|
4438
|
+
{
|
|
4439
|
+
type: "button",
|
|
4440
|
+
"data-keep-action": "use-server",
|
|
4441
|
+
onClick: () => void view.resolve(conflict, "remote"),
|
|
4442
|
+
disabled: view.busyId !== void 0,
|
|
4443
|
+
children: view.labels.useServer
|
|
4444
|
+
}
|
|
4445
|
+
),
|
|
4446
|
+
/* @__PURE__ */ jsx24(
|
|
4447
|
+
"button",
|
|
4448
|
+
{
|
|
4449
|
+
type: "button",
|
|
4450
|
+
"data-keep-action": "manual-merge",
|
|
4451
|
+
onClick: () => void view.resolve(conflict, "manual"),
|
|
4452
|
+
disabled: view.busyId !== void 0 || !onManualMerge,
|
|
4453
|
+
children: view.labels.manualMerge
|
|
4454
|
+
}
|
|
4455
|
+
)
|
|
4456
|
+
] })
|
|
4457
|
+
] }, conflict.id))
|
|
4458
|
+
] }) : null,
|
|
4459
|
+
view.showBackupRecovery ? /* @__PURE__ */ jsxs18("section", { "data-recovery": "backup", children: [
|
|
4460
|
+
/* @__PURE__ */ jsx24("h3", { children: view.labels.backupRecovery }),
|
|
4461
|
+
/* @__PURE__ */ jsx24("p", { children: view.labels.backupRecoveryDescription }),
|
|
4462
|
+
backup ?? (showBackupControls ? /* @__PURE__ */ jsx24(KeepBackup, {}) : null)
|
|
4463
|
+
] }) : null,
|
|
4464
|
+
view.error ? /* @__PURE__ */ jsx24("p", { id: "keepkit-sync-recovery-error", role: "alert", "aria-live": "assertive", children: view.error instanceof Error ? view.error.message : view.labels.error }) : null
|
|
4465
|
+
]
|
|
4466
|
+
}
|
|
4467
|
+
);
|
|
4468
|
+
}
|
|
4469
|
+
function ConflictPreview({
|
|
4470
|
+
item,
|
|
4471
|
+
heading,
|
|
4472
|
+
updatedAtLabel,
|
|
4473
|
+
noteLabel,
|
|
4474
|
+
side
|
|
4475
|
+
}) {
|
|
4476
|
+
return /* @__PURE__ */ jsxs18("article", { "data-conflict-version": side, "aria-label": heading, children: [
|
|
4477
|
+
/* @__PURE__ */ jsx24("h4", { children: heading }),
|
|
4478
|
+
/* @__PURE__ */ jsxs18("dl", { children: [
|
|
4479
|
+
/* @__PURE__ */ jsxs18("div", { children: [
|
|
4480
|
+
/* @__PURE__ */ jsx24("dt", { children: updatedAtLabel }),
|
|
4481
|
+
/* @__PURE__ */ jsx24("dd", { children: item ? /* @__PURE__ */ jsx24("time", { dateTime: new Date(item.updatedAt).toISOString(), children: formatConflictDate(item.updatedAt) }) : "\u2014" })
|
|
4482
|
+
] }),
|
|
4483
|
+
/* @__PURE__ */ jsxs18("div", { children: [
|
|
4484
|
+
/* @__PURE__ */ jsx24("dt", { children: noteLabel }),
|
|
4485
|
+
/* @__PURE__ */ jsx24("dd", { children: item?.note || "\u2014" })
|
|
4486
|
+
] })
|
|
4487
|
+
] })
|
|
4488
|
+
] });
|
|
4489
|
+
}
|
|
4490
|
+
function formatConflictDate(timestamp) {
|
|
4491
|
+
return new Date(timestamp).toISOString().slice(0, 10);
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4494
|
+
// src/features/sync/hooks/useKeepSyncStatusBanner.ts
|
|
4495
|
+
import { useKeepContext as useKeepContext7 } from "@keepkit/core/react";
|
|
4496
|
+
function useKeepSyncStatusBanner({ onRetry, children }) {
|
|
4497
|
+
const context = useKeepContext7();
|
|
4498
|
+
const retryLabel = useUiLabel("retrySync");
|
|
4499
|
+
const resolveLabel = useUiLabel("resolveSync");
|
|
4500
|
+
const conflictLabel = useUiLabel("syncConflict");
|
|
4501
|
+
const pendingLabel = useUiLabel("syncPending");
|
|
4502
|
+
const syncedLabel = useUiLabel("syncSynced");
|
|
4503
|
+
const status = context.syncState.status;
|
|
4504
|
+
const hasConflicts = (context.syncState.conflicts?.length ?? 0) > 0 || context.syncState.conflictIds.length > 0;
|
|
4505
|
+
const message = children ?? (status === "error" ? getErrorMessage5(context.syncState.error) : status === "conflict" || hasConflicts ? conflictLabel : status === "pending" || status === "syncing" ? pendingLabel : syncedLabel);
|
|
4506
|
+
return {
|
|
4507
|
+
status,
|
|
4508
|
+
hasConflicts,
|
|
4509
|
+
message,
|
|
4510
|
+
isMutating: context.isMutating,
|
|
4511
|
+
role: status === "error" || status === "conflict" || hasConflicts ? "alert" : "status",
|
|
4512
|
+
showRetry: status === "error" || status === "pending" || status === "syncing",
|
|
4513
|
+
retryLabel,
|
|
4514
|
+
resolveLabel,
|
|
4515
|
+
retry: async () => {
|
|
4516
|
+
if (onRetry) {
|
|
4517
|
+
await onRetry();
|
|
4518
|
+
return;
|
|
4519
|
+
}
|
|
4520
|
+
await context.flushSync();
|
|
4521
|
+
}
|
|
4522
|
+
};
|
|
4523
|
+
}
|
|
4524
|
+
function getErrorMessage5(error) {
|
|
4525
|
+
return error instanceof Error ? error.message : "Sync failed.";
|
|
4526
|
+
}
|
|
4527
|
+
|
|
4528
|
+
// src/features/sync/KeepSyncStatusBanner.tsx
|
|
4529
|
+
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
4530
|
+
function KeepSyncStatusBanner({
|
|
4531
|
+
onRetry,
|
|
4532
|
+
onResolveConflicts,
|
|
4533
|
+
children,
|
|
4534
|
+
className,
|
|
4535
|
+
...props
|
|
4536
|
+
}) {
|
|
4537
|
+
const view = useKeepSyncStatusBanner({ onRetry, children });
|
|
4538
|
+
if (view.status === "idle" && !view.hasConflicts) return null;
|
|
4539
|
+
return /* @__PURE__ */ jsxs19(
|
|
4540
|
+
"aside",
|
|
4541
|
+
{
|
|
4542
|
+
...props,
|
|
4543
|
+
className,
|
|
4544
|
+
role: props.role ?? view.role,
|
|
4545
|
+
"aria-live": props["aria-live"] ?? "polite",
|
|
4546
|
+
"data-keepkit": "sync-status",
|
|
4547
|
+
"data-state": view.status,
|
|
4548
|
+
children: [
|
|
4549
|
+
/* @__PURE__ */ jsx25("p", { children: view.message }),
|
|
4550
|
+
view.showRetry ? /* @__PURE__ */ jsx25(
|
|
4551
|
+
"button",
|
|
4552
|
+
{
|
|
4553
|
+
type: "button",
|
|
4554
|
+
"data-keep-action": "retry-sync",
|
|
4555
|
+
onClick: () => void view.retry(),
|
|
4556
|
+
disabled: view.isMutating,
|
|
4557
|
+
children: view.retryLabel
|
|
4558
|
+
}
|
|
4559
|
+
) : null,
|
|
4560
|
+
view.hasConflicts ? /* @__PURE__ */ jsx25(
|
|
4561
|
+
"button",
|
|
4562
|
+
{
|
|
4563
|
+
type: "button",
|
|
4564
|
+
"data-keep-action": "resolve-conflicts",
|
|
4565
|
+
onClick: onResolveConflicts,
|
|
4566
|
+
disabled: !onResolveConflicts,
|
|
4567
|
+
children: view.resolveLabel
|
|
4568
|
+
}
|
|
4569
|
+
) : null
|
|
4570
|
+
]
|
|
4571
|
+
}
|
|
4572
|
+
);
|
|
4573
|
+
}
|
|
4574
|
+
|
|
4575
|
+
// src/foundation/theme.tsx
|
|
4576
|
+
import { isValidElement as isValidElement9 } from "react";
|
|
4577
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
4103
4578
|
var keepThemeNames = [
|
|
4104
4579
|
"default",
|
|
4105
4580
|
"ocean",
|
|
@@ -4154,11 +4629,11 @@ function KeepThemeProvider({
|
|
|
4154
4629
|
"data-reduced-motion": reducedMotion ? "true" : void 0
|
|
4155
4630
|
};
|
|
4156
4631
|
if (asChild) {
|
|
4157
|
-
if (!
|
|
4632
|
+
if (!isValidElement9(children))
|
|
4158
4633
|
throw new Error("KeepThemeProvider with asChild requires a single React element child.");
|
|
4159
|
-
return
|
|
4634
|
+
return createSlot(children, rootProps);
|
|
4160
4635
|
}
|
|
4161
|
-
return /* @__PURE__ */
|
|
4636
|
+
return /* @__PURE__ */ jsx26("div", { ...rootProps, children });
|
|
4162
4637
|
}
|
|
4163
4638
|
|
|
4164
4639
|
// src/index.tsx
|
|
@@ -4182,7 +4657,7 @@ import {
|
|
|
4182
4657
|
LocalStorageSyncQueueAdapter,
|
|
4183
4658
|
SyncStorageAdapter
|
|
4184
4659
|
} from "@keepkit/core/storage";
|
|
4185
|
-
import { jsx as
|
|
4660
|
+
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
4186
4661
|
function KeepKitProvider({
|
|
4187
4662
|
labels,
|
|
4188
4663
|
locale,
|
|
@@ -4202,7 +4677,7 @@ function KeepKitProvider({
|
|
|
4202
4677
|
children,
|
|
4203
4678
|
...providerProps
|
|
4204
4679
|
}) {
|
|
4205
|
-
return /* @__PURE__ */
|
|
4680
|
+
return /* @__PURE__ */ jsx27(KeepUiProvider, { labels, locale, labelResolver, onFeedback, children: /* @__PURE__ */ jsx27(
|
|
4206
4681
|
KeepThemeProvider,
|
|
4207
4682
|
{
|
|
4208
4683
|
theme,
|
|
@@ -4216,10 +4691,10 @@ function KeepKitProvider({
|
|
|
4216
4691
|
className: themeClassName,
|
|
4217
4692
|
style: themeStyle,
|
|
4218
4693
|
asChild: themeAsChild,
|
|
4219
|
-
children: /* @__PURE__ */
|
|
4220
|
-
/* @__PURE__ */
|
|
4694
|
+
children: /* @__PURE__ */ jsxs20(CoreKeepProvider, { ...providerProps, children: [
|
|
4695
|
+
/* @__PURE__ */ jsx27(KeepSyncFeedbackObserver, {}),
|
|
4221
4696
|
children,
|
|
4222
|
-
/* @__PURE__ */
|
|
4697
|
+
/* @__PURE__ */ jsx27(KeepAnnouncements, {})
|
|
4223
4698
|
] })
|
|
4224
4699
|
}
|
|
4225
4700
|
) });
|
|
@@ -4246,7 +4721,7 @@ function createKeepKit(options = {}) {
|
|
|
4246
4721
|
} = options;
|
|
4247
4722
|
const coreKit = createCoreKeepKit(coreOptions);
|
|
4248
4723
|
return {
|
|
4249
|
-
Provider: (props) => /* @__PURE__ */
|
|
4724
|
+
Provider: (props) => /* @__PURE__ */ jsx27(
|
|
4250
4725
|
KeepKitProvider,
|
|
4251
4726
|
{
|
|
4252
4727
|
...coreOptions,
|
|
@@ -4267,9 +4742,9 @@ function createKeepKit(options = {}) {
|
|
|
4267
4742
|
...props
|
|
4268
4743
|
}
|
|
4269
4744
|
),
|
|
4270
|
-
Button: (props) => /* @__PURE__ */
|
|
4271
|
-
Backup: (props) => /* @__PURE__ */
|
|
4272
|
-
Collection: (props) => /* @__PURE__ */
|
|
4745
|
+
Button: (props) => /* @__PURE__ */ jsx27(KeepButton, { ...props }),
|
|
4746
|
+
Backup: (props) => /* @__PURE__ */ jsx27(KeepBackup, { ...props }),
|
|
4747
|
+
Collection: (props) => /* @__PURE__ */ jsx27(
|
|
4273
4748
|
KeepCollection,
|
|
4274
4749
|
{
|
|
4275
4750
|
...props,
|
|
@@ -4291,6 +4766,7 @@ export {
|
|
|
4291
4766
|
FallbackStorageAdapter,
|
|
4292
4767
|
IndexedDBAdapter,
|
|
4293
4768
|
IndexedDBSyncQueueAdapter,
|
|
4769
|
+
KeepActiveFiltersSummary,
|
|
4294
4770
|
KeepAnnouncements,
|
|
4295
4771
|
KeepAnnouncer,
|
|
4296
4772
|
KeepBackup,
|
|
@@ -4314,6 +4790,7 @@ export {
|
|
|
4314
4790
|
KeepPruneStaleButton,
|
|
4315
4791
|
KeepReorderableList,
|
|
4316
4792
|
KeepSearchInput,
|
|
4793
|
+
KeepShortcutHint,
|
|
4317
4794
|
KeepSortSelect,
|
|
4318
4795
|
KeepStaleNotice,
|
|
4319
4796
|
KeepStatus,
|
|
@@ -4328,15 +4805,18 @@ export {
|
|
|
4328
4805
|
LocalStorageAdapter,
|
|
4329
4806
|
LocalStorageSyncQueueAdapter,
|
|
4330
4807
|
SyncStorageAdapter,
|
|
4808
|
+
chainedFunction,
|
|
4331
4809
|
createAuthenticatedSyncKit,
|
|
4332
4810
|
createBrowserStorageAdapter,
|
|
4333
4811
|
createKeepKit,
|
|
4334
4812
|
createNextPagesRouterAdapter,
|
|
4813
|
+
createSlot,
|
|
4335
4814
|
createStorageAdapter,
|
|
4336
4815
|
getKeepLocaleLabels,
|
|
4337
4816
|
highlightText,
|
|
4338
4817
|
isAllSelected,
|
|
4339
4818
|
keepThemeNames,
|
|
4819
|
+
mergeProps,
|
|
4340
4820
|
toggleSelectAll,
|
|
4341
4821
|
useKeepContext8 as useKeepContext,
|
|
4342
4822
|
useKeepItem5 as useKeepItem,
|