@keepkit/ui 0.13.0 → 0.15.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 +17 -7
- package/dist/index.d.ts +14 -11
- package/dist/index.js +1355 -808
- package/dist/index.js.map +1 -1
- package/dist/styles/base.css +270 -17
- package/dist/styles/button.css +19 -2
- package/dist/styles/collection.css +286 -31
- package/dist/styles/sync.css +138 -7
- package/dist/tailwind.css +2 -0
- package/dist/tailwind.d.ts +4 -0
- package/dist/tailwind.js +5 -1
- package/dist/tailwind.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
createKeepKit as createCoreKeepKit
|
|
7
7
|
} from "@keepkit/core/react";
|
|
8
8
|
|
|
9
|
-
// src/
|
|
9
|
+
// src/hooks/useKeepBackup.ts
|
|
10
10
|
import { useKeepContext } from "@keepkit/core/react";
|
|
11
11
|
import { useRef, useState } from "react";
|
|
12
12
|
|
|
@@ -55,6 +55,9 @@ var DE_LABELS = {
|
|
|
55
55
|
currentPage: "Aktuelle Seite",
|
|
56
56
|
searchResults: "Aktuelle Suchergebnisse",
|
|
57
57
|
allItems: "Alle gespeicherten Elemente",
|
|
58
|
+
localVersion: "Lokal",
|
|
59
|
+
remoteVersion: "Server",
|
|
60
|
+
updatedAt: "Aktualisiert",
|
|
58
61
|
statusAvailable: "Verf\xFCgbar",
|
|
59
62
|
noteSavedMessage: "Notiz gespeichert.",
|
|
60
63
|
exportData: "JSON exportieren",
|
|
@@ -128,6 +131,9 @@ var EN_LABELS = {
|
|
|
128
131
|
currentPage: "Current page",
|
|
129
132
|
searchResults: "Current search results",
|
|
130
133
|
allItems: "All saved items",
|
|
134
|
+
localVersion: "Local",
|
|
135
|
+
remoteVersion: "Remote",
|
|
136
|
+
updatedAt: "Updated",
|
|
131
137
|
statusAvailable: "Available",
|
|
132
138
|
noteSavedMessage: "Note saved.",
|
|
133
139
|
exportData: "Export JSON",
|
|
@@ -201,6 +207,9 @@ var ES_LABELS = {
|
|
|
201
207
|
currentPage: "P\xE1gina actual",
|
|
202
208
|
searchResults: "Resultados de b\xFAsqueda actuales",
|
|
203
209
|
allItems: "Todos los elementos guardados",
|
|
210
|
+
localVersion: "Local",
|
|
211
|
+
remoteVersion: "Servidor",
|
|
212
|
+
updatedAt: "Actualizado",
|
|
204
213
|
statusAvailable: "Disponible",
|
|
205
214
|
noteSavedMessage: "Nota guardada.",
|
|
206
215
|
exportData: "Exportar JSON",
|
|
@@ -274,6 +283,9 @@ var FIL_LABELS = {
|
|
|
274
283
|
currentPage: "Kasalukuyang pahina",
|
|
275
284
|
searchResults: "Kasalukuyang resulta ng paghahanap",
|
|
276
285
|
allItems: "Lahat ng naka-save na item",
|
|
286
|
+
localVersion: "Lokal",
|
|
287
|
+
remoteVersion: "Remote",
|
|
288
|
+
updatedAt: "Na-update",
|
|
277
289
|
statusAvailable: "Available",
|
|
278
290
|
noteSavedMessage: "Na-save ang tala.",
|
|
279
291
|
exportData: "I-export ang JSON",
|
|
@@ -347,6 +359,9 @@ var FR_LABELS = {
|
|
|
347
359
|
currentPage: "Page actuelle",
|
|
348
360
|
searchResults: "R\xE9sultats de recherche actuels",
|
|
349
361
|
allItems: "Tous les \xE9l\xE9ments enregistr\xE9s",
|
|
362
|
+
localVersion: "Local",
|
|
363
|
+
remoteVersion: "Distant",
|
|
364
|
+
updatedAt: "Mis \xE0 jour",
|
|
350
365
|
statusAvailable: "Disponible",
|
|
351
366
|
noteSavedMessage: "Note enregistr\xE9e.",
|
|
352
367
|
exportData: "Exporter JSON",
|
|
@@ -420,6 +435,9 @@ var ID_LABELS = {
|
|
|
420
435
|
currentPage: "Halaman saat ini",
|
|
421
436
|
searchResults: "Hasil pencarian saat ini",
|
|
422
437
|
allItems: "Semua item tersimpan",
|
|
438
|
+
localVersion: "Lokal",
|
|
439
|
+
remoteVersion: "Server",
|
|
440
|
+
updatedAt: "Diperbarui",
|
|
423
441
|
statusAvailable: "Tersedia",
|
|
424
442
|
noteSavedMessage: "Catatan tersimpan.",
|
|
425
443
|
exportData: "Ekspor JSON",
|
|
@@ -493,6 +511,9 @@ var IT_LABELS = {
|
|
|
493
511
|
currentPage: "Pagina corrente",
|
|
494
512
|
searchResults: "Risultati di ricerca correnti",
|
|
495
513
|
allItems: "Tutti gli elementi salvati",
|
|
514
|
+
localVersion: "Locale",
|
|
515
|
+
remoteVersion: "Server",
|
|
516
|
+
updatedAt: "Aggiornato",
|
|
496
517
|
statusAvailable: "Disponibile",
|
|
497
518
|
noteSavedMessage: "Nota salvata.",
|
|
498
519
|
exportData: "Esporta JSON",
|
|
@@ -566,6 +587,9 @@ var JA_LABELS = {
|
|
|
566
587
|
currentPage: "\u8868\u793A\u4E2D\u306E\u30DA\u30FC\u30B8",
|
|
567
588
|
searchResults: "\u73FE\u5728\u306E\u691C\u7D22\u7D50\u679C\u5168\u4F53",
|
|
568
589
|
allItems: "\u3059\u3079\u3066\u306E\u4FDD\u5B58\u30A2\u30A4\u30C6\u30E0",
|
|
590
|
+
localVersion: "\u30ED\u30FC\u30AB\u30EB",
|
|
591
|
+
remoteVersion: "\u30EA\u30E2\u30FC\u30C8",
|
|
592
|
+
updatedAt: "\u66F4\u65B0\u65E5\u6642",
|
|
569
593
|
statusAvailable: "\u5229\u7528\u53EF\u80FD",
|
|
570
594
|
noteSavedMessage: "\u30E1\u30E2\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F\u3002",
|
|
571
595
|
exportData: "JSON\u3092\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8",
|
|
@@ -639,6 +663,9 @@ var KO_LABELS = {
|
|
|
639
663
|
currentPage: "\uD604\uC7AC \uD398\uC774\uC9C0",
|
|
640
664
|
searchResults: "\uD604\uC7AC \uAC80\uC0C9 \uACB0\uACFC \uC804\uCCB4",
|
|
641
665
|
allItems: "\uBAA8\uB4E0 \uC800\uC7A5 \uD56D\uBAA9",
|
|
666
|
+
localVersion: "\uB85C\uCEEC",
|
|
667
|
+
remoteVersion: "\uC6D0\uACA9",
|
|
668
|
+
updatedAt: "\uC5C5\uB370\uC774\uD2B8 \uC2DC\uAC04",
|
|
642
669
|
statusAvailable: "\uC0AC\uC6A9 \uAC00\uB2A5",
|
|
643
670
|
noteSavedMessage: "\uBA54\uBAA8\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4.",
|
|
644
671
|
exportData: "JSON \uB0B4\uBCF4\uB0B4\uAE30",
|
|
@@ -712,6 +739,9 @@ var MS_LABELS = {
|
|
|
712
739
|
currentPage: "Halaman semasa",
|
|
713
740
|
searchResults: "Hasil carian semasa",
|
|
714
741
|
allItems: "Semua item yang disimpan",
|
|
742
|
+
localVersion: "Tempatan",
|
|
743
|
+
remoteVersion: "Pelayan",
|
|
744
|
+
updatedAt: "Dikemas kini",
|
|
715
745
|
statusAvailable: "Tersedia",
|
|
716
746
|
noteSavedMessage: "Nota disimpan.",
|
|
717
747
|
exportData: "Eksport JSON",
|
|
@@ -785,6 +815,9 @@ var PT_BR_LABELS = {
|
|
|
785
815
|
currentPage: "P\xE1gina atual",
|
|
786
816
|
searchResults: "Resultados da pesquisa atual",
|
|
787
817
|
allItems: "Todos os itens salvos",
|
|
818
|
+
localVersion: "Local",
|
|
819
|
+
remoteVersion: "Servidor",
|
|
820
|
+
updatedAt: "Atualizado",
|
|
788
821
|
statusAvailable: "Dispon\xEDvel",
|
|
789
822
|
noteSavedMessage: "Nota salva.",
|
|
790
823
|
exportData: "Exportar JSON",
|
|
@@ -858,6 +891,9 @@ var RU_LABELS = {
|
|
|
858
891
|
currentPage: "\u0422\u0435\u043A\u0443\u0449\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430",
|
|
859
892
|
searchResults: "\u0422\u0435\u043A\u0443\u0449\u0438\u0435 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E\u0438\u0441\u043A\u0430",
|
|
860
893
|
allItems: "\u0412\u0441\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B",
|
|
894
|
+
localVersion: "\u041B\u043E\u043A\u0430\u043B\u044C\u043D\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F",
|
|
895
|
+
remoteVersion: "\u0421\u0435\u0440\u0432\u0435\u0440\u043D\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F",
|
|
896
|
+
updatedAt: "\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u043E",
|
|
861
897
|
statusAvailable: "\u0414\u043E\u0441\u0442\u0443\u043F\u0435\u043D",
|
|
862
898
|
noteSavedMessage: "\u0417\u0430\u043C\u0435\u0442\u043A\u0430 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0430.",
|
|
863
899
|
exportData: "\u042D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C JSON",
|
|
@@ -931,6 +967,9 @@ var TH_LABELS = {
|
|
|
931
967
|
currentPage: "\u0E2B\u0E19\u0E49\u0E32\u0E1B\u0E31\u0E08\u0E08\u0E38\u0E1A\u0E31\u0E19",
|
|
932
968
|
searchResults: "\u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E1B\u0E31\u0E08\u0E08\u0E38\u0E1A\u0E31\u0E19",
|
|
933
969
|
allItems: "\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23\u0E17\u0E35\u0E48\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
|
|
970
|
+
localVersion: "\u0E43\u0E19\u0E40\u0E04\u0E23\u0E37\u0E48\u0E2D\u0E07",
|
|
971
|
+
remoteVersion: "\u0E23\u0E30\u0E22\u0E30\u0E44\u0E01\u0E25",
|
|
972
|
+
updatedAt: "\u0E2D\u0E31\u0E1B\u0E40\u0E14\u0E15\u0E40\u0E21\u0E37\u0E48\u0E2D",
|
|
934
973
|
statusAvailable: "\u0E1E\u0E23\u0E49\u0E2D\u0E21\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19",
|
|
935
974
|
noteSavedMessage: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E22\u0E48\u0E2D\u0E41\u0E25\u0E49\u0E27",
|
|
936
975
|
exportData: "\u0E2A\u0E48\u0E07\u0E2D\u0E2D\u0E01 JSON",
|
|
@@ -1004,6 +1043,9 @@ var VI_LABELS = {
|
|
|
1004
1043
|
currentPage: "Trang hi\u1EC7n t\u1EA1i",
|
|
1005
1044
|
searchResults: "K\u1EBFt qu\u1EA3 t\xECm ki\u1EBFm hi\u1EC7n t\u1EA1i",
|
|
1006
1045
|
allItems: "T\u1EA5t c\u1EA3 m\u1EE5c \u0111\xE3 l\u01B0u",
|
|
1046
|
+
localVersion: "C\u1EE5c b\u1ED9",
|
|
1047
|
+
remoteVersion: "M\xE1y ch\u1EE7",
|
|
1048
|
+
updatedAt: "\u0110\xE3 c\u1EADp nh\u1EADt",
|
|
1007
1049
|
statusAvailable: "Kh\u1EA3 d\u1EE5ng",
|
|
1008
1050
|
noteSavedMessage: "\u0110\xE3 l\u01B0u ghi ch\xFA.",
|
|
1009
1051
|
exportData: "Xu\u1EA5t JSON",
|
|
@@ -1077,6 +1119,9 @@ var ZH_HANS_LABELS = {
|
|
|
1077
1119
|
currentPage: "\u5F53\u524D\u9875",
|
|
1078
1120
|
searchResults: "\u5F53\u524D\u5168\u90E8\u641C\u7D22\u7ED3\u679C",
|
|
1079
1121
|
allItems: "\u5168\u90E8\u5DF2\u4FDD\u5B58\u9879\u76EE",
|
|
1122
|
+
localVersion: "\u672C\u5730",
|
|
1123
|
+
remoteVersion: "\u8FDC\u7A0B",
|
|
1124
|
+
updatedAt: "\u66F4\u65B0\u65F6\u95F4",
|
|
1080
1125
|
statusAvailable: "\u53EF\u7528",
|
|
1081
1126
|
noteSavedMessage: "\u5907\u6CE8\u5DF2\u4FDD\u5B58\u3002",
|
|
1082
1127
|
exportData: "\u5BFC\u51FAJSON",
|
|
@@ -1150,6 +1195,9 @@ var ZH_HANT_LABELS = {
|
|
|
1150
1195
|
currentPage: "\u76EE\u524D\u9801\u9762",
|
|
1151
1196
|
searchResults: "\u76EE\u524D\u5168\u90E8\u641C\u5C0B\u7D50\u679C",
|
|
1152
1197
|
allItems: "\u6240\u6709\u5DF2\u5132\u5B58\u9805\u76EE",
|
|
1198
|
+
localVersion: "\u672C\u6A5F",
|
|
1199
|
+
remoteVersion: "\u9060\u7AEF",
|
|
1200
|
+
updatedAt: "\u66F4\u65B0\u6642\u9593",
|
|
1153
1201
|
statusAvailable: "\u53EF\u7528",
|
|
1154
1202
|
noteSavedMessage: "\u5099\u8A3B\u5DF2\u5132\u5B58\u3002",
|
|
1155
1203
|
exportData: "\u532F\u51FAJSON",
|
|
@@ -1261,28 +1309,14 @@ function useUiLabel(key, override) {
|
|
|
1261
1309
|
return override ?? context.customLabels?.[key] ?? context.labelResolver?.(key, { locale: context.locale }) ?? context.labels[key];
|
|
1262
1310
|
}
|
|
1263
1311
|
|
|
1264
|
-
// src/
|
|
1265
|
-
|
|
1266
|
-
function KeepBackup({
|
|
1267
|
-
filename = "keepkit-backup.json",
|
|
1268
|
-
onExport,
|
|
1269
|
-
onImported,
|
|
1270
|
-
...props
|
|
1271
|
-
}) {
|
|
1312
|
+
// src/hooks/useKeepBackup.ts
|
|
1313
|
+
function useKeepBackup({ filename, onExport, onImported }) {
|
|
1272
1314
|
const context = useKeepContext();
|
|
1273
|
-
const exportLabel = useUiLabel("exportData");
|
|
1274
|
-
const importLabel = useUiLabel("importData");
|
|
1275
|
-
const importModeLabel = useUiLabel("importMode");
|
|
1276
|
-
const mergeLabel = useUiLabel("merge");
|
|
1277
|
-
const replaceLabel = useUiLabel("replace");
|
|
1278
|
-
const importedCountLabel = useUiLabel("importedCount");
|
|
1279
|
-
const failedCountLabel = useUiLabel("failedCount");
|
|
1280
|
-
const quotaErrorLabel = useUiLabel("storageQuotaError");
|
|
1281
1315
|
const inputRef = useRef(null);
|
|
1282
1316
|
const [mode, setMode] = useState("merge");
|
|
1283
1317
|
const [result, setResult] = useState();
|
|
1284
1318
|
const [error, setError] = useState();
|
|
1285
|
-
async function
|
|
1319
|
+
async function exportBackup() {
|
|
1286
1320
|
setError(void 0);
|
|
1287
1321
|
try {
|
|
1288
1322
|
const data = await context.exportBackup();
|
|
@@ -1298,7 +1332,7 @@ function KeepBackup({
|
|
|
1298
1332
|
setError(cause);
|
|
1299
1333
|
}
|
|
1300
1334
|
}
|
|
1301
|
-
async function
|
|
1335
|
+
async function importBackup(event) {
|
|
1302
1336
|
const file = event.currentTarget.files?.[0];
|
|
1303
1337
|
event.currentTarget.value = "";
|
|
1304
1338
|
if (!file) return;
|
|
@@ -1312,43 +1346,93 @@ function KeepBackup({
|
|
|
1312
1346
|
setError(cause);
|
|
1313
1347
|
}
|
|
1314
1348
|
}
|
|
1349
|
+
return {
|
|
1350
|
+
inputRef,
|
|
1351
|
+
mode,
|
|
1352
|
+
setMode,
|
|
1353
|
+
result,
|
|
1354
|
+
error,
|
|
1355
|
+
isMutating: context.isMutating,
|
|
1356
|
+
exportBackup,
|
|
1357
|
+
importBackup,
|
|
1358
|
+
openFilePicker: () => inputRef.current?.click(),
|
|
1359
|
+
labels: {
|
|
1360
|
+
export: useUiLabel("exportData"),
|
|
1361
|
+
import: useUiLabel("importData"),
|
|
1362
|
+
importMode: useUiLabel("importMode"),
|
|
1363
|
+
merge: useUiLabel("merge"),
|
|
1364
|
+
replace: useUiLabel("replace"),
|
|
1365
|
+
importedCount: useUiLabel("importedCount"),
|
|
1366
|
+
failedCount: useUiLabel("failedCount"),
|
|
1367
|
+
quotaError: useUiLabel("storageQuotaError")
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/KeepBackup.tsx
|
|
1373
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1374
|
+
function KeepBackup({
|
|
1375
|
+
filename = "keepkit-backup.json",
|
|
1376
|
+
onExport,
|
|
1377
|
+
onImported,
|
|
1378
|
+
...props
|
|
1379
|
+
}) {
|
|
1380
|
+
const view = useKeepBackup({ filename, onExport, onImported });
|
|
1315
1381
|
return /* @__PURE__ */ jsxs(
|
|
1316
1382
|
"section",
|
|
1317
1383
|
{
|
|
1318
1384
|
...props,
|
|
1319
1385
|
"data-keepkit": "backup",
|
|
1320
|
-
"data-state": error ? "error" : result ? "complete" : "idle",
|
|
1321
|
-
"data-loading":
|
|
1386
|
+
"data-state": view.error ? "error" : view.result ? "complete" : "idle",
|
|
1387
|
+
"data-loading": view.isMutating ? "true" : void 0,
|
|
1322
1388
|
children: [
|
|
1323
|
-
/* @__PURE__ */ jsx2(
|
|
1389
|
+
/* @__PURE__ */ jsx2(
|
|
1390
|
+
"button",
|
|
1391
|
+
{
|
|
1392
|
+
type: "button",
|
|
1393
|
+
"data-keep-action": "export-backup",
|
|
1394
|
+
onClick: () => void view.exportBackup(),
|
|
1395
|
+
disabled: view.isMutating,
|
|
1396
|
+
children: view.labels.export
|
|
1397
|
+
}
|
|
1398
|
+
),
|
|
1324
1399
|
/* @__PURE__ */ jsxs("label", { children: [
|
|
1325
|
-
|
|
1326
|
-
/* @__PURE__ */ jsxs(
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1400
|
+
view.labels.importMode,
|
|
1401
|
+
/* @__PURE__ */ jsxs(
|
|
1402
|
+
"select",
|
|
1403
|
+
{
|
|
1404
|
+
"data-keep-action": "select-import-mode",
|
|
1405
|
+
value: view.mode,
|
|
1406
|
+
onChange: (event) => view.setMode(event.currentTarget.value),
|
|
1407
|
+
children: [
|
|
1408
|
+
/* @__PURE__ */ jsx2("option", { value: "merge", children: view.labels.merge }),
|
|
1409
|
+
/* @__PURE__ */ jsx2("option", { value: "replace", children: view.labels.replace })
|
|
1410
|
+
]
|
|
1411
|
+
}
|
|
1412
|
+
)
|
|
1330
1413
|
] }),
|
|
1331
|
-
/* @__PURE__ */ jsx2("button", { type: "button",
|
|
1414
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "data-keep-action": "import-backup", onClick: view.openFilePicker, disabled: view.isMutating, children: view.labels.import }),
|
|
1332
1415
|
/* @__PURE__ */ jsx2(
|
|
1333
1416
|
"input",
|
|
1334
1417
|
{
|
|
1335
|
-
ref: inputRef,
|
|
1418
|
+
ref: view.inputRef,
|
|
1336
1419
|
type: "file",
|
|
1420
|
+
"data-keep-action": "select-backup-file",
|
|
1337
1421
|
accept: "application/json,.json",
|
|
1338
|
-
"aria-label":
|
|
1339
|
-
onChange: (event) => void
|
|
1422
|
+
"aria-label": view.labels.import,
|
|
1423
|
+
onChange: (event) => void view.importBackup(event)
|
|
1340
1424
|
}
|
|
1341
1425
|
),
|
|
1342
|
-
result ? /* @__PURE__ */ jsxs("p", { role: "status", children: [
|
|
1343
|
-
result.imported,
|
|
1426
|
+
view.result ? /* @__PURE__ */ jsxs("p", { role: "status", children: [
|
|
1427
|
+
view.result.imported,
|
|
1344
1428
|
" ",
|
|
1345
|
-
|
|
1429
|
+
view.labels.importedCount,
|
|
1346
1430
|
"; ",
|
|
1347
|
-
result.failed,
|
|
1431
|
+
view.result.failed,
|
|
1348
1432
|
" ",
|
|
1349
|
-
|
|
1433
|
+
view.labels.failedCount
|
|
1350
1434
|
] }) : null,
|
|
1351
|
-
error ? /* @__PURE__ */ jsx2("p", { role: "alert", children: isQuotaError(error) ?
|
|
1435
|
+
view.error ? /* @__PURE__ */ jsx2("p", { role: "alert", children: isQuotaError(view.error) ? view.labels.quotaError : getErrorMessage(view.error) }) : null
|
|
1352
1436
|
]
|
|
1353
1437
|
}
|
|
1354
1438
|
);
|
|
@@ -1362,48 +1446,16 @@ function getErrorMessage(error) {
|
|
|
1362
1446
|
return error instanceof Error ? error.message : "Something went wrong.";
|
|
1363
1447
|
}
|
|
1364
1448
|
|
|
1365
|
-
// src/
|
|
1449
|
+
// src/hooks/useKeepBulkActions.ts
|
|
1366
1450
|
import { useKeepList } from "@keepkit/core/react";
|
|
1367
1451
|
import { useState as useState2 } from "react";
|
|
1368
1452
|
|
|
1369
|
-
// src/KeepItemCheckbox.tsx
|
|
1370
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1371
|
-
function KeepItemCheckbox({
|
|
1372
|
-
item,
|
|
1373
|
-
checked = false,
|
|
1374
|
-
label,
|
|
1375
|
-
onCheckedChange,
|
|
1376
|
-
"aria-label": ariaLabel,
|
|
1377
|
-
...props
|
|
1378
|
-
}) {
|
|
1379
|
-
const itemLabel = getItemLabel(item) ?? item.id;
|
|
1380
|
-
const accessibleLabel = ariaLabel ?? (typeof label === "string" ? label : itemLabel);
|
|
1381
|
-
return /* @__PURE__ */ jsx3(
|
|
1382
|
-
"input",
|
|
1383
|
-
{
|
|
1384
|
-
...props,
|
|
1385
|
-
type: "checkbox",
|
|
1386
|
-
checked,
|
|
1387
|
-
"data-keepkit": "item-checkbox",
|
|
1388
|
-
"data-state": checked ? "checked" : "unchecked",
|
|
1389
|
-
"data-disabled": props.disabled ? "true" : void 0,
|
|
1390
|
-
"aria-label": accessibleLabel,
|
|
1391
|
-
onChange: (event) => onCheckedChange?.(event.currentTarget.checked)
|
|
1392
|
-
}
|
|
1393
|
-
);
|
|
1394
|
-
}
|
|
1395
|
-
function getItemLabel(item) {
|
|
1396
|
-
if (typeof item.meta !== "object" || item.meta === null || !("title" in item.meta)) return void 0;
|
|
1397
|
-
const title = item.meta.title;
|
|
1398
|
-
return typeof title === "string" && title.trim() ? title.trim() : void 0;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
1453
|
// src/shared.tsx
|
|
1402
1454
|
import {
|
|
1403
1455
|
cloneElement,
|
|
1404
1456
|
isValidElement
|
|
1405
1457
|
} from "react";
|
|
1406
|
-
import { jsx as
|
|
1458
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1407
1459
|
function toKeepButtonItem(item) {
|
|
1408
1460
|
return {
|
|
1409
1461
|
id: item.id,
|
|
@@ -1432,11 +1484,10 @@ function renderRoot(asChild, child, props, body, componentName) {
|
|
|
1432
1484
|
if (!isValidElement(child)) throw new Error(`${componentName} with asChild requires a single React element child.`);
|
|
1433
1485
|
return cloneElement(child, { ...props, children: body });
|
|
1434
1486
|
}
|
|
1435
|
-
return /* @__PURE__ */
|
|
1487
|
+
return /* @__PURE__ */ jsx3("div", { ...props, children: body });
|
|
1436
1488
|
}
|
|
1437
1489
|
|
|
1438
|
-
// src/
|
|
1439
|
-
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1490
|
+
// src/hooks/useKeepBulkActions.ts
|
|
1440
1491
|
function isAllSelected(items, selectedIds) {
|
|
1441
1492
|
if (items.length === 0) return false;
|
|
1442
1493
|
const selected = new Set(selectedIds);
|
|
@@ -1451,42 +1502,30 @@ function toggleSelectAll(items, selectedIds) {
|
|
|
1451
1502
|
}
|
|
1452
1503
|
return nextIds;
|
|
1453
1504
|
}
|
|
1454
|
-
function
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
children,
|
|
1465
|
-
...props
|
|
1466
|
-
}) {
|
|
1467
|
-
const selectItemsLabel = useUiLabel("selectItems");
|
|
1468
|
-
const selectedCountLabel = useUiLabel("selectedCount");
|
|
1469
|
-
const deleteSelectedLabel = useUiLabel("deleteSelected");
|
|
1470
|
-
const tagsLabel = useUiLabel("tagsToApply");
|
|
1471
|
-
const applyTagsLabel = useUiLabel("applyTags");
|
|
1472
|
-
const selectionScopeLabel = useUiLabel("selectionScope");
|
|
1473
|
-
const currentPageLabel = useUiLabel("currentPage");
|
|
1474
|
-
const searchResultsLabel = useUiLabel("searchResults");
|
|
1475
|
-
const allItemsLabel = useUiLabel("allItems");
|
|
1505
|
+
function useKeepBulkActions(options) {
|
|
1506
|
+
const {
|
|
1507
|
+
query,
|
|
1508
|
+
controlledSelectedIds,
|
|
1509
|
+
defaultSelectedIds,
|
|
1510
|
+
onSelectedIdsChange,
|
|
1511
|
+
onCompleted,
|
|
1512
|
+
controlledScope,
|
|
1513
|
+
onSelectionScopeChange
|
|
1514
|
+
} = options;
|
|
1476
1515
|
const list = useKeepList(query);
|
|
1477
1516
|
const queryList = useKeepList(query ? { ...query, pagination: void 0 } : { pagination: void 0 });
|
|
1478
1517
|
const allList = useKeepList({ pagination: void 0 });
|
|
1479
1518
|
const [uncontrolledScope, setUncontrolledScope] = useState2(controlledScope ?? "page");
|
|
1480
1519
|
const selectionScope = controlledScope ?? uncontrolledScope;
|
|
1481
|
-
const setSelectionScope = (scope) => {
|
|
1482
|
-
if (controlledScope === void 0) setUncontrolledScope(scope);
|
|
1483
|
-
onSelectionScopeChange?.(scope);
|
|
1484
|
-
};
|
|
1485
1520
|
const targetItems = selectionScope === "page" ? list.items : selectionScope === "query" ? queryList.items : allList.items;
|
|
1486
1521
|
const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState2(defaultSelectedIds);
|
|
1487
1522
|
const selectedIds = controlledSelectedIds ?? uncontrolledSelectedIds;
|
|
1488
1523
|
const selected = new Set(selectedIds);
|
|
1489
1524
|
const [tagsInput, setTagsInput] = useState2("");
|
|
1525
|
+
const setSelectionScope = (scope) => {
|
|
1526
|
+
if (controlledScope === void 0) setUncontrolledScope(scope);
|
|
1527
|
+
onSelectionScopeChange?.(scope);
|
|
1528
|
+
};
|
|
1490
1529
|
const setSelectedIds = (ids) => {
|
|
1491
1530
|
if (controlledSelectedIds === void 0) setUncontrolledSelectedIds(ids);
|
|
1492
1531
|
onSelectedIdsChange?.(ids);
|
|
@@ -1523,54 +1562,156 @@ function KeepBulkActions({
|
|
|
1523
1562
|
selectionScope,
|
|
1524
1563
|
setSelectionScope
|
|
1525
1564
|
};
|
|
1565
|
+
return {
|
|
1566
|
+
state,
|
|
1567
|
+
selected,
|
|
1568
|
+
isLoading: list.isLoading,
|
|
1569
|
+
labels: {
|
|
1570
|
+
selectItems: useUiLabel("selectItems"),
|
|
1571
|
+
selectedCount: useUiLabel("selectedCount"),
|
|
1572
|
+
deleteSelected: useUiLabel("deleteSelected"),
|
|
1573
|
+
tags: useUiLabel("tagsToApply"),
|
|
1574
|
+
applyTags: useUiLabel("applyTags"),
|
|
1575
|
+
selectionScope: useUiLabel("selectionScope"),
|
|
1576
|
+
currentPage: useUiLabel("currentPage"),
|
|
1577
|
+
searchResults: useUiLabel("searchResults"),
|
|
1578
|
+
allItems: useUiLabel("allItems")
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// src/KeepItemCheckbox.tsx
|
|
1584
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1585
|
+
function KeepItemCheckbox({
|
|
1586
|
+
item,
|
|
1587
|
+
checked = false,
|
|
1588
|
+
label,
|
|
1589
|
+
onCheckedChange,
|
|
1590
|
+
"aria-label": ariaLabel,
|
|
1591
|
+
...props
|
|
1592
|
+
}) {
|
|
1593
|
+
const itemLabel = getItemLabel(item) ?? item.id;
|
|
1594
|
+
const accessibleLabel = ariaLabel ?? (typeof label === "string" ? label : itemLabel);
|
|
1595
|
+
return /* @__PURE__ */ jsx4(
|
|
1596
|
+
"input",
|
|
1597
|
+
{
|
|
1598
|
+
...props,
|
|
1599
|
+
type: "checkbox",
|
|
1600
|
+
checked,
|
|
1601
|
+
"data-keepkit": "item-checkbox",
|
|
1602
|
+
"data-keep-action": "select-item",
|
|
1603
|
+
"data-state": checked ? "checked" : "unchecked",
|
|
1604
|
+
"data-disabled": props.disabled ? "true" : void 0,
|
|
1605
|
+
"aria-label": accessibleLabel,
|
|
1606
|
+
onChange: (event) => onCheckedChange?.(event.currentTarget.checked)
|
|
1607
|
+
}
|
|
1608
|
+
);
|
|
1609
|
+
}
|
|
1610
|
+
function getItemLabel(item) {
|
|
1611
|
+
if (typeof item.meta !== "object" || item.meta === null || !("title" in item.meta)) return void 0;
|
|
1612
|
+
const title = item.meta.title;
|
|
1613
|
+
return typeof title === "string" && title.trim() ? title.trim() : void 0;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// src/KeepBulkActions.tsx
|
|
1617
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1618
|
+
function KeepBulkActions({
|
|
1619
|
+
query,
|
|
1620
|
+
selectedIds: controlledSelectedIds,
|
|
1621
|
+
defaultSelectedIds = [],
|
|
1622
|
+
onSelectedIdsChange,
|
|
1623
|
+
renderItem,
|
|
1624
|
+
onCompleted,
|
|
1625
|
+
selectionScope: controlledScope,
|
|
1626
|
+
onSelectionScopeChange,
|
|
1627
|
+
render,
|
|
1628
|
+
children,
|
|
1629
|
+
...props
|
|
1630
|
+
}) {
|
|
1631
|
+
const { state, selected, isLoading, labels } = useKeepBulkActions({
|
|
1632
|
+
query,
|
|
1633
|
+
controlledSelectedIds,
|
|
1634
|
+
defaultSelectedIds,
|
|
1635
|
+
onSelectedIdsChange,
|
|
1636
|
+
onCompleted,
|
|
1637
|
+
controlledScope,
|
|
1638
|
+
onSelectionScopeChange
|
|
1639
|
+
});
|
|
1526
1640
|
const body = render ? render(state) : typeof children === "function" ? children(state) : children ?? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1527
1641
|
/* @__PURE__ */ jsxs2("fieldset", { children: [
|
|
1528
|
-
/* @__PURE__ */ jsx5("legend", { children:
|
|
1642
|
+
/* @__PURE__ */ jsx5("legend", { children: labels.selectItems }),
|
|
1529
1643
|
/* @__PURE__ */ jsxs2("label", { children: [
|
|
1530
|
-
|
|
1644
|
+
labels.selectionScope,
|
|
1531
1645
|
/* @__PURE__ */ jsxs2(
|
|
1532
1646
|
"select",
|
|
1533
1647
|
{
|
|
1534
|
-
|
|
1535
|
-
|
|
1648
|
+
"data-keep-action": "select-scope",
|
|
1649
|
+
value: state.selectionScope,
|
|
1650
|
+
onChange: (event) => state.setSelectionScope(event.currentTarget.value),
|
|
1536
1651
|
children: [
|
|
1537
|
-
/* @__PURE__ */ jsx5("option", { value: "page", children:
|
|
1538
|
-
/* @__PURE__ */ jsx5("option", { value: "query", children:
|
|
1539
|
-
/* @__PURE__ */ jsx5("option", { value: "all", children:
|
|
1652
|
+
/* @__PURE__ */ jsx5("option", { value: "page", children: labels.currentPage }),
|
|
1653
|
+
/* @__PURE__ */ jsx5("option", { value: "query", children: labels.searchResults }),
|
|
1654
|
+
/* @__PURE__ */ jsx5("option", { value: "all", children: labels.allItems })
|
|
1540
1655
|
]
|
|
1541
1656
|
}
|
|
1542
1657
|
)
|
|
1543
1658
|
] }),
|
|
1544
1659
|
/* @__PURE__ */ jsxs2("label", { children: [
|
|
1545
|
-
/* @__PURE__ */ jsx5(
|
|
1546
|
-
|
|
1660
|
+
/* @__PURE__ */ jsx5(
|
|
1661
|
+
"input",
|
|
1662
|
+
{
|
|
1663
|
+
type: "checkbox",
|
|
1664
|
+
"data-keep-action": "select-all",
|
|
1665
|
+
checked: state.allSelected,
|
|
1666
|
+
onChange: state.toggleAll,
|
|
1667
|
+
"aria-label": labels.selectItems
|
|
1668
|
+
}
|
|
1669
|
+
),
|
|
1670
|
+
state.selectedIds.length,
|
|
1547
1671
|
" ",
|
|
1548
|
-
|
|
1672
|
+
labels.selectedCount
|
|
1549
1673
|
] }),
|
|
1550
|
-
|
|
1674
|
+
state.items.map((item) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
1551
1675
|
/* @__PURE__ */ jsx5(
|
|
1552
1676
|
KeepItemCheckbox,
|
|
1553
1677
|
{
|
|
1554
1678
|
item,
|
|
1555
1679
|
checked: selected.has(item.id),
|
|
1556
|
-
onCheckedChange: () => toggle(item.id)
|
|
1680
|
+
onCheckedChange: () => state.toggle(item.id)
|
|
1557
1681
|
}
|
|
1558
1682
|
),
|
|
1559
1683
|
renderItem ? renderItem(item, selected.has(item.id)) : getMetaTitle(item.meta) ?? item.id
|
|
1560
1684
|
] }, item.id))
|
|
1561
1685
|
] }),
|
|
1562
|
-
/* @__PURE__ */ jsx5(
|
|
1686
|
+
/* @__PURE__ */ jsx5(
|
|
1687
|
+
"button",
|
|
1688
|
+
{
|
|
1689
|
+
type: "button",
|
|
1690
|
+
"data-keep-action": "delete-selected",
|
|
1691
|
+
onClick: () => void state.remove(),
|
|
1692
|
+
disabled: state.selectedIds.length === 0 || state.isMutating,
|
|
1693
|
+
children: labels.deleteSelected
|
|
1694
|
+
}
|
|
1695
|
+
),
|
|
1563
1696
|
/* @__PURE__ */ jsxs2("label", { children: [
|
|
1564
|
-
|
|
1565
|
-
/* @__PURE__ */ jsx5(
|
|
1697
|
+
labels.tags,
|
|
1698
|
+
/* @__PURE__ */ jsx5(
|
|
1699
|
+
"input",
|
|
1700
|
+
{
|
|
1701
|
+
"data-keep-action": "edit-tags",
|
|
1702
|
+
value: state.tagsInput,
|
|
1703
|
+
onChange: (event) => state.setTagsInput(event.currentTarget.value)
|
|
1704
|
+
}
|
|
1705
|
+
)
|
|
1566
1706
|
] }),
|
|
1567
1707
|
/* @__PURE__ */ jsx5(
|
|
1568
1708
|
"button",
|
|
1569
1709
|
{
|
|
1570
1710
|
type: "button",
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1711
|
+
"data-keep-action": "apply-tags",
|
|
1712
|
+
onClick: () => void state.updateTags(),
|
|
1713
|
+
disabled: state.selectedIds.length === 0 || state.isMutating,
|
|
1714
|
+
children: labels.applyTags
|
|
1574
1715
|
}
|
|
1575
1716
|
)
|
|
1576
1717
|
] });
|
|
@@ -1579,9 +1720,9 @@ function KeepBulkActions({
|
|
|
1579
1720
|
{
|
|
1580
1721
|
...props,
|
|
1581
1722
|
"data-keepkit": "bulk-actions",
|
|
1582
|
-
"aria-busy":
|
|
1583
|
-
"data-state": selectedIds.length > 0 ? "selected" : "idle",
|
|
1584
|
-
"data-loading":
|
|
1723
|
+
"aria-busy": state.isMutating || props["aria-busy"],
|
|
1724
|
+
"data-state": state.selectedIds.length > 0 ? "selected" : "idle",
|
|
1725
|
+
"data-loading": isLoading || state.isMutating ? "true" : void 0,
|
|
1585
1726
|
children: body
|
|
1586
1727
|
}
|
|
1587
1728
|
);
|
|
@@ -1589,10 +1730,26 @@ function KeepBulkActions({
|
|
|
1589
1730
|
|
|
1590
1731
|
// src/KeepButton.tsx
|
|
1591
1732
|
import {
|
|
1592
|
-
KeepButton as CoreKeepButton
|
|
1593
|
-
useKeepItem
|
|
1733
|
+
KeepButton as CoreKeepButton
|
|
1594
1734
|
} from "@keepkit/core/react";
|
|
1595
1735
|
import { createElement } from "react";
|
|
1736
|
+
|
|
1737
|
+
// src/hooks/useKeepButton.ts
|
|
1738
|
+
import { useKeepItem } from "@keepkit/core/react";
|
|
1739
|
+
function useKeepButton({ item, labels, icons, children }) {
|
|
1740
|
+
return {
|
|
1741
|
+
buttonState: useKeepItem(item),
|
|
1742
|
+
customStateLabel: labels?.loading !== void 0 || labels?.error !== void 0 || icons !== void 0 && children === void 0,
|
|
1743
|
+
labels: {
|
|
1744
|
+
save: useUiLabel("save", typeof labels?.unsaved === "string" ? labels.unsaved : void 0),
|
|
1745
|
+
saved: useUiLabel("saved", typeof labels?.saved === "string" ? labels.saved : void 0),
|
|
1746
|
+
loading: useUiLabel("loading", typeof labels?.loading === "string" ? labels.loading : void 0),
|
|
1747
|
+
error: useUiLabel("error", typeof labels?.error === "string" ? labels.error : void 0)
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
// src/KeepButton.tsx
|
|
1596
1753
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1597
1754
|
function KeepButton({
|
|
1598
1755
|
labels,
|
|
@@ -1602,16 +1759,11 @@ function KeepButton({
|
|
|
1602
1759
|
iconClassName,
|
|
1603
1760
|
...props
|
|
1604
1761
|
}) {
|
|
1605
|
-
const
|
|
1606
|
-
const savedLabel = useUiLabel("saved", typeof labels?.saved === "string" ? labels.saved : void 0);
|
|
1607
|
-
const loadingLabel = useUiLabel("loading", typeof labels?.loading === "string" ? labels.loading : void 0);
|
|
1608
|
-
const errorLabel = useUiLabel("error", typeof labels?.error === "string" ? labels.error : void 0);
|
|
1609
|
-
const buttonState = useKeepItem(props.item);
|
|
1610
|
-
const customStateLabel = labels?.loading !== void 0 || labels?.error !== void 0 || icons !== void 0 && props.children === void 0;
|
|
1762
|
+
const view = useKeepButton({ item: props.item, labels, icons, children: props.children });
|
|
1611
1763
|
const getStateContent = (state) => {
|
|
1612
1764
|
if (typeof props.children === "function") return props.children(state);
|
|
1613
1765
|
if (props.children !== void 0) return props.children;
|
|
1614
|
-
const label = state.error ? labels?.error ??
|
|
1766
|
+
const label = state.error ? labels?.error ?? view.labels.error : state.isMutating ? labels?.loading ?? view.labels.loading : state.isSaved ? labels?.saved ?? view.labels.saved : labels?.unsaved ?? view.labels.save;
|
|
1615
1767
|
if (!icons) return label;
|
|
1616
1768
|
const icon = state.error ? icons.error : state.isMutating ? icons.loading : state.isSaved ? icons.remove ?? icons.saved : icons.save;
|
|
1617
1769
|
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
@@ -1622,14 +1774,16 @@ function KeepButton({
|
|
|
1622
1774
|
const sharedProps = {
|
|
1623
1775
|
...props,
|
|
1624
1776
|
"data-keepkit": "button",
|
|
1777
|
+
"data-keep-action": "toggle-save",
|
|
1778
|
+
"data-has-custom-icon": icons ? "true" : void 0,
|
|
1625
1779
|
"data-icon-only": iconOnly ? "true" : void 0,
|
|
1626
|
-
"aria-busy": props["aria-busy"] ?? (buttonState.isLoading || buttonState.isMutating),
|
|
1627
|
-
savedLabel: labels?.saved ?? props.savedLabel ??
|
|
1628
|
-
unsavedLabel: labels?.unsaved ?? props.unsavedLabel ??
|
|
1780
|
+
"aria-busy": props["aria-busy"] ?? (view.buttonState.isLoading || view.buttonState.isMutating),
|
|
1781
|
+
savedLabel: labels?.saved ?? props.savedLabel ?? view.labels.saved,
|
|
1782
|
+
unsavedLabel: labels?.unsaved ?? props.unsavedLabel ?? view.labels.save,
|
|
1629
1783
|
savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,
|
|
1630
1784
|
unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel
|
|
1631
1785
|
};
|
|
1632
|
-
if (!customStateLabel) return /* @__PURE__ */ jsx6(CoreKeepButton, { ...sharedProps });
|
|
1786
|
+
if (!view.customStateLabel) return /* @__PURE__ */ jsx6(CoreKeepButton, { ...sharedProps });
|
|
1633
1787
|
return /* @__PURE__ */ jsx6(CoreKeepButton, { ...sharedProps, children: (state) => /* @__PURE__ */ jsx6(Fragment2, { children: getStateContent(state) }) });
|
|
1634
1788
|
}
|
|
1635
1789
|
function renderIcon(icon, className) {
|
|
@@ -1638,114 +1792,416 @@ function renderIcon(icon, className) {
|
|
|
1638
1792
|
}
|
|
1639
1793
|
|
|
1640
1794
|
// src/KeepCollection.tsx
|
|
1641
|
-
import { KeepErrorBoundary as KeepErrorBoundary2
|
|
1642
|
-
import { useMemo as useMemo3, useState as useState6 } from "react";
|
|
1795
|
+
import { KeepErrorBoundary as KeepErrorBoundary2 } from "@keepkit/core/react";
|
|
1643
1796
|
|
|
1644
|
-
// src/
|
|
1645
|
-
import {
|
|
1646
|
-
|
|
1647
|
-
useKeepList as useKeepList2
|
|
1648
|
-
} from "@keepkit/core/react";
|
|
1649
|
-
import { isValidElement as isValidElement3 } from "react";
|
|
1797
|
+
// src/hooks/useKeepCollection.ts
|
|
1798
|
+
import { useKeepList as useKeepList2 } from "@keepkit/core/react";
|
|
1799
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
1650
1800
|
|
|
1651
|
-
// src/
|
|
1652
|
-
import { useKeepItem as useKeepItem2 } from "@keepkit/core/react";
|
|
1801
|
+
// src/url-sync.tsx
|
|
1653
1802
|
import {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
import {
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
switch (status) {
|
|
1670
|
-
case "available":
|
|
1671
|
-
return "statusAvailable";
|
|
1672
|
-
case "expired":
|
|
1673
|
-
return "statusExpired";
|
|
1674
|
-
case "removed":
|
|
1675
|
-
return "statusRemoved";
|
|
1676
|
-
case "deleted":
|
|
1677
|
-
return "statusDeleted";
|
|
1678
|
-
case "private":
|
|
1679
|
-
case "unknown":
|
|
1680
|
-
return status === "private" ? "statusPrivate" : "statusUnknown";
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
// src/KeepStaleNotice.tsx
|
|
1685
|
-
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1686
|
-
function KeepStaleNotice({
|
|
1687
|
-
item,
|
|
1688
|
-
onRetry,
|
|
1689
|
-
onRemoved,
|
|
1690
|
-
retryLabel,
|
|
1691
|
-
removeLabel,
|
|
1692
|
-
children,
|
|
1693
|
-
className,
|
|
1694
|
-
...props
|
|
1695
|
-
}) {
|
|
1696
|
-
const context = useKeepContext2();
|
|
1697
|
-
const retryText = useUiLabel("retry");
|
|
1698
|
-
const removeText = useUiLabel("removeFromList");
|
|
1699
|
-
const errorText = useUiLabel("error");
|
|
1700
|
-
const [isRetrying, setIsRetrying] = useState3(false);
|
|
1701
|
-
const [error, setError] = useState3(null);
|
|
1702
|
-
const status = item.status && item.status !== "available" ? item.status : "unknown";
|
|
1703
|
-
async function handleRetry() {
|
|
1704
|
-
setError(null);
|
|
1705
|
-
setIsRetrying(true);
|
|
1706
|
-
try {
|
|
1707
|
-
if (onRetry) await onRetry(item);
|
|
1708
|
-
else await context.revalidateItems();
|
|
1709
|
-
} catch (cause) {
|
|
1710
|
-
setError(cause);
|
|
1711
|
-
} finally {
|
|
1712
|
-
setIsRetrying(false);
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
async function handleRemove() {
|
|
1716
|
-
try {
|
|
1717
|
-
await context.removeItemWithUndo(item.id);
|
|
1718
|
-
onRemoved?.(item);
|
|
1719
|
-
} catch (cause) {
|
|
1720
|
-
setError(cause);
|
|
1803
|
+
DEFAULT_KEEP_URL_PARAMS,
|
|
1804
|
+
decodeKeepListQuery,
|
|
1805
|
+
encodeKeepListQuery
|
|
1806
|
+
} from "@keepkit/core/core";
|
|
1807
|
+
import { useEffect, useRef as useRef2 } from "react";
|
|
1808
|
+
function createNextPagesRouterAdapter(router) {
|
|
1809
|
+
const getUrl = () => router.asPath ?? (typeof window === "undefined" ? "/" : window.location.href);
|
|
1810
|
+
return {
|
|
1811
|
+
getUrl,
|
|
1812
|
+
subscribe: router.events ? (listener) => {
|
|
1813
|
+
router.events?.on("routeChangeComplete", listener);
|
|
1814
|
+
return () => router.events?.off("routeChangeComplete", listener);
|
|
1815
|
+
} : void 0,
|
|
1816
|
+
navigate: (url, mode) => {
|
|
1817
|
+
void router[mode](url, void 0, { shallow: true });
|
|
1721
1818
|
}
|
|
1722
|
-
}
|
|
1723
|
-
return /* @__PURE__ */ jsxs4("aside", { ...props, className, "data-keepkit": "stale-notice", "data-state": error ? "error" : "stale", children: [
|
|
1724
|
-
/* @__PURE__ */ jsx8(KeepItemStatusBadge, { status }),
|
|
1725
|
-
children ?? (item.statusReason ? /* @__PURE__ */ jsx8("p", { children: item.statusReason }) : null),
|
|
1726
|
-
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1727
|
-
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void handleRetry(), disabled: isRetrying || context.isMutating, children: retryLabel ?? retryText }),
|
|
1728
|
-
/* @__PURE__ */ jsx8("button", { type: "button", onClick: () => void handleRemove(), disabled: context.isMutating, children: removeLabel ?? removeText })
|
|
1729
|
-
] }),
|
|
1730
|
-
error ? /* @__PURE__ */ jsx8("p", { role: "alert", children: error instanceof Error ? error.message : errorText }) : null
|
|
1731
|
-
] });
|
|
1819
|
+
};
|
|
1732
1820
|
}
|
|
1733
|
-
function
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
const
|
|
1742
|
-
const
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1821
|
+
function useKeepUrlSync({
|
|
1822
|
+
enabled = true,
|
|
1823
|
+
query,
|
|
1824
|
+
onQueryChange,
|
|
1825
|
+
options = {},
|
|
1826
|
+
adapter: providedAdapter
|
|
1827
|
+
}) {
|
|
1828
|
+
const browserAdapterRef = useRef2(getBrowserAdapter());
|
|
1829
|
+
const adapter = providedAdapter ?? browserAdapterRef.current;
|
|
1830
|
+
const onQueryChangeRef = useRef2(onQueryChange);
|
|
1831
|
+
onQueryChangeRef.current = onQueryChange;
|
|
1832
|
+
const skipWriteRef = useRef2(true);
|
|
1833
|
+
const params = options.params;
|
|
1834
|
+
useEffect(() => {
|
|
1835
|
+
if (!enabled) return;
|
|
1836
|
+
const read = () => {
|
|
1837
|
+
const url = adapter.getUrl();
|
|
1838
|
+
const decoded = decodeKeepListQuery(url, { params });
|
|
1839
|
+
skipWriteRef.current = true;
|
|
1840
|
+
onQueryChangeRef.current((previousQuery) => ({
|
|
1841
|
+
...previousQuery,
|
|
1842
|
+
...decoded.search ? { search: decoded.search } : { search: void 0 },
|
|
1843
|
+
...decoded.tags ? { tags: decoded.tags } : { tags: void 0 },
|
|
1844
|
+
...decoded.sort ? { sort: decoded.sort } : {},
|
|
1845
|
+
...decoded.pagination ? { pagination: { ...previousQuery.pagination, ...decoded.pagination } } : {}
|
|
1846
|
+
}));
|
|
1847
|
+
};
|
|
1848
|
+
read();
|
|
1849
|
+
return adapter.subscribe?.(read);
|
|
1850
|
+
}, [adapter, enabled, params]);
|
|
1851
|
+
useEffect(() => {
|
|
1852
|
+
if (!enabled) return;
|
|
1853
|
+
if (skipWriteRef.current) {
|
|
1854
|
+
skipWriteRef.current = false;
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
const currentUrl = new URL(adapter.getUrl(), "http://keepkit.invalid");
|
|
1858
|
+
const urlParams = { ...DEFAULT_KEEP_URL_PARAMS, ...params };
|
|
1859
|
+
for (const key of Object.values(urlParams)) currentUrl.searchParams.delete(key);
|
|
1860
|
+
const nextParams = encodeKeepListQuery(query, { params });
|
|
1861
|
+
nextParams.forEach((value, key) => {
|
|
1862
|
+
currentUrl.searchParams.append(key, value);
|
|
1863
|
+
});
|
|
1864
|
+
const nextUrl = `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`;
|
|
1865
|
+
adapter.navigate(nextUrl, options.history ?? "push");
|
|
1866
|
+
}, [adapter, enabled, options.history, params, query]);
|
|
1867
|
+
}
|
|
1868
|
+
function getBrowserAdapter() {
|
|
1869
|
+
return {
|
|
1870
|
+
getUrl: () => typeof window === "undefined" ? "/" : window.location.href,
|
|
1871
|
+
subscribe: (listener) => {
|
|
1872
|
+
if (typeof window === "undefined") return () => void 0;
|
|
1873
|
+
window.addEventListener("popstate", listener);
|
|
1874
|
+
return () => window.removeEventListener("popstate", listener);
|
|
1875
|
+
},
|
|
1876
|
+
navigate: (url, mode) => {
|
|
1877
|
+
if (typeof window === "undefined") return;
|
|
1878
|
+
window.history[mode === "push" ? "pushState" : "replaceState"]({}, "", url);
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
// src/hooks/useKeepCollection.ts
|
|
1884
|
+
function useKeepCollection({
|
|
1885
|
+
query,
|
|
1886
|
+
pageSize,
|
|
1887
|
+
urlSync,
|
|
1888
|
+
urlAdapter,
|
|
1889
|
+
features
|
|
1890
|
+
}) {
|
|
1891
|
+
const enabled = {
|
|
1892
|
+
search: true,
|
|
1893
|
+
sort: true,
|
|
1894
|
+
pagination: true,
|
|
1895
|
+
tagFilter: false,
|
|
1896
|
+
bulkActions: false,
|
|
1897
|
+
...features
|
|
1898
|
+
};
|
|
1899
|
+
const [searchValue, setSearchValue] = useState3(query.search?.query ?? "");
|
|
1900
|
+
const [sort, setSort] = useState3(query.sort ?? { by: "updatedAt", direction: "desc" });
|
|
1901
|
+
const [tag, setTag] = useState3(query.tags?.[0]);
|
|
1902
|
+
const [page, setPage] = useState3(query.pagination?.page ?? 1);
|
|
1903
|
+
const resolvedPageSize = query.pagination?.pageSize ?? pageSize;
|
|
1904
|
+
const resolvedQuery = useMemo2(
|
|
1905
|
+
() => ({
|
|
1906
|
+
...query,
|
|
1907
|
+
search: enabled.search ? { ...query.search, query: searchValue } : query.search,
|
|
1908
|
+
sort: enabled.sort ? sort : query.sort,
|
|
1909
|
+
tags: tag ? [.../* @__PURE__ */ new Set([...query.tags ?? [], tag])] : query.tags,
|
|
1910
|
+
pagination: enabled.pagination ? { ...query.pagination, page, pageSize: resolvedPageSize } : query.pagination
|
|
1911
|
+
}),
|
|
1912
|
+
[enabled.pagination, enabled.search, enabled.sort, page, query, resolvedPageSize, searchValue, sort, tag]
|
|
1913
|
+
);
|
|
1914
|
+
useKeepUrlSync({
|
|
1915
|
+
enabled: Boolean(urlSync),
|
|
1916
|
+
query: resolvedQuery,
|
|
1917
|
+
onQueryChange: (nextOrUpdater) => {
|
|
1918
|
+
const next = typeof nextOrUpdater === "function" ? nextOrUpdater(resolvedQuery) : nextOrUpdater;
|
|
1919
|
+
setSearchValue(next.search?.query ?? "");
|
|
1920
|
+
setSort(next.sort ?? { by: "updatedAt", direction: "desc" });
|
|
1921
|
+
setTag(next.tags?.[0]);
|
|
1922
|
+
setPage(next.pagination?.page ?? 1);
|
|
1923
|
+
},
|
|
1924
|
+
options: typeof urlSync === "object" ? urlSync : {},
|
|
1925
|
+
adapter: urlAdapter
|
|
1926
|
+
});
|
|
1927
|
+
const list = useKeepList2(resolvedQuery);
|
|
1928
|
+
return {
|
|
1929
|
+
enabled,
|
|
1930
|
+
searchValue,
|
|
1931
|
+
sortValue: sortToValue(sort),
|
|
1932
|
+
tag,
|
|
1933
|
+
resolvedPageSize,
|
|
1934
|
+
resolvedQuery,
|
|
1935
|
+
list,
|
|
1936
|
+
setSearchValue: (value) => {
|
|
1937
|
+
setSearchValue(value);
|
|
1938
|
+
setPage(1);
|
|
1939
|
+
},
|
|
1940
|
+
setSortValue: (_value, nextSort) => {
|
|
1941
|
+
setSort(nextSort);
|
|
1942
|
+
setPage(1);
|
|
1943
|
+
},
|
|
1944
|
+
setTag: (value) => {
|
|
1945
|
+
setTag(value);
|
|
1946
|
+
setPage(1);
|
|
1947
|
+
},
|
|
1948
|
+
setPage
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// src/KeepList.tsx
|
|
1953
|
+
import { KeepErrorBoundary } from "@keepkit/core/react";
|
|
1954
|
+
import { isValidElement as isValidElement3 } from "react";
|
|
1955
|
+
|
|
1956
|
+
// src/hooks/useKeepListView.ts
|
|
1957
|
+
import { useKeepList as useKeepList3 } from "@keepkit/core/react";
|
|
1958
|
+
function useKeepListView(query) {
|
|
1959
|
+
return {
|
|
1960
|
+
state: useKeepList3(query),
|
|
1961
|
+
labels: {
|
|
1962
|
+
loading: useUiLabel("loadingItems"),
|
|
1963
|
+
empty: useUiLabel("noItems"),
|
|
1964
|
+
error: useUiLabel("errorItems")
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
// src/KeepItemCard.tsx
|
|
1970
|
+
import {
|
|
1971
|
+
isValidElement as isValidElement2
|
|
1972
|
+
} from "react";
|
|
1973
|
+
|
|
1974
|
+
// src/hooks/useKeepItemCard.ts
|
|
1975
|
+
import { useKeepItem as useKeepItem2 } from "@keepkit/core/react";
|
|
1976
|
+
function useKeepItemCard(options) {
|
|
1977
|
+
const {
|
|
1978
|
+
item,
|
|
1979
|
+
title,
|
|
1980
|
+
getTitle,
|
|
1981
|
+
getImageProps,
|
|
1982
|
+
href: hrefOption,
|
|
1983
|
+
linkTargetAttribute,
|
|
1984
|
+
linkRel,
|
|
1985
|
+
onRemoveError,
|
|
1986
|
+
onRemoved
|
|
1987
|
+
} = options;
|
|
1988
|
+
const itemState = useKeepItem2(item);
|
|
1989
|
+
const resolvedTitle = typeof title === "function" ? title(item) : title ?? getTitle?.(item) ?? getMetaTitle(item.meta) ?? item.id;
|
|
1990
|
+
const imageProps = getImageProps?.(item, resolvedTitle);
|
|
1991
|
+
const href = typeof hrefOption === "function" ? hrefOption(item) : hrefOption;
|
|
1992
|
+
const isAvailable = item.status === void 0 || item.status === "available";
|
|
1993
|
+
const isExternalLink = href ? /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(href) : false;
|
|
1994
|
+
const statusLabelKey = item.status && item.status !== "available" ? getStatusLabelKey(item.status) : "statusUnknown";
|
|
1995
|
+
const unavailableLabel = useUiLabel(statusLabelKey);
|
|
1996
|
+
const state = {
|
|
1997
|
+
item,
|
|
1998
|
+
isSaved: itemState.isSaved,
|
|
1999
|
+
isMutating: itemState.isMutating,
|
|
2000
|
+
error: itemState.error,
|
|
2001
|
+
remove: itemState.remove,
|
|
2002
|
+
status: item.status
|
|
2003
|
+
};
|
|
2004
|
+
return {
|
|
2005
|
+
itemState,
|
|
2006
|
+
state,
|
|
2007
|
+
resolvedTitle,
|
|
2008
|
+
imageProps,
|
|
2009
|
+
href,
|
|
2010
|
+
isAvailable,
|
|
2011
|
+
displayStatus: getDisplayStatus(item.status),
|
|
2012
|
+
resolvedLinkTarget: linkTargetAttribute ?? (isExternalLink ? "_blank" : void 0),
|
|
2013
|
+
resolvedLinkRel: linkRel ?? (isExternalLink ? "noreferrer" : void 0),
|
|
2014
|
+
statusLabel: item.status && item.status !== "available" ? unavailableLabel : void 0,
|
|
2015
|
+
remove: async () => {
|
|
2016
|
+
try {
|
|
2017
|
+
await itemState.remove();
|
|
2018
|
+
onRemoved?.(item);
|
|
2019
|
+
} catch (cause) {
|
|
2020
|
+
onRemoveError?.(cause);
|
|
2021
|
+
}
|
|
2022
|
+
},
|
|
2023
|
+
labels: {
|
|
2024
|
+
save: useUiLabel("save"),
|
|
2025
|
+
savedAt: useUiLabel("saved"),
|
|
2026
|
+
error: useUiLabel("error"),
|
|
2027
|
+
remove: useUiLabel("remove"),
|
|
2028
|
+
tags: useUiLabel("tags")
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
function getStatusLabelKey(status) {
|
|
2033
|
+
switch (status) {
|
|
2034
|
+
case "expired":
|
|
2035
|
+
return "statusExpired";
|
|
2036
|
+
case "removed":
|
|
2037
|
+
return "statusRemoved";
|
|
2038
|
+
case "deleted":
|
|
2039
|
+
return "statusDeleted";
|
|
2040
|
+
case "private":
|
|
2041
|
+
return "statusPrivate";
|
|
2042
|
+
default:
|
|
2043
|
+
return "statusUnknown";
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
function getDisplayStatus(status) {
|
|
2047
|
+
if (status === void 0 || status === "available") return "available";
|
|
2048
|
+
if (status === "expired") return "expired";
|
|
2049
|
+
if (status === "removed") return "removed";
|
|
2050
|
+
return "restricted";
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
// src/hooks/useKeepStaleNotice.ts
|
|
2054
|
+
import { useKeepContext as useKeepContext2 } from "@keepkit/core/react";
|
|
2055
|
+
import { useState as useState4 } from "react";
|
|
2056
|
+
function useKeepStaleNotice({ item, onRetry, onRemoved }) {
|
|
2057
|
+
const context = useKeepContext2();
|
|
2058
|
+
const [isRetrying, setIsRetrying] = useState4(false);
|
|
2059
|
+
const [error, setError] = useState4(null);
|
|
2060
|
+
async function retry() {
|
|
2061
|
+
setError(null);
|
|
2062
|
+
setIsRetrying(true);
|
|
2063
|
+
try {
|
|
2064
|
+
if (onRetry) await onRetry(item);
|
|
2065
|
+
else await context.revalidateItems();
|
|
2066
|
+
} catch (cause) {
|
|
2067
|
+
setError(cause);
|
|
2068
|
+
} finally {
|
|
2069
|
+
setIsRetrying(false);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
async function remove() {
|
|
2073
|
+
try {
|
|
2074
|
+
await context.removeItemWithUndo(item.id);
|
|
2075
|
+
onRemoved?.(item);
|
|
2076
|
+
} catch (cause) {
|
|
2077
|
+
setError(cause);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
return {
|
|
2081
|
+
status: item.status && item.status !== "available" ? item.status : "unknown",
|
|
2082
|
+
error,
|
|
2083
|
+
isRetrying,
|
|
2084
|
+
isMutating: context.isMutating,
|
|
2085
|
+
retry,
|
|
2086
|
+
remove,
|
|
2087
|
+
labels: {
|
|
2088
|
+
retry: useUiLabel("retry"),
|
|
2089
|
+
remove: useUiLabel("removeFromList"),
|
|
2090
|
+
error: useUiLabel("error")
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function useKeepPruneStale({ statuses, onPruned }) {
|
|
2095
|
+
const context = useKeepContext2();
|
|
2096
|
+
const staleIds = context.items.filter((item) => item.status && statuses.includes(item.status)).map((item) => item.id);
|
|
2097
|
+
return {
|
|
2098
|
+
staleIds,
|
|
2099
|
+
isMutating: context.isMutating,
|
|
2100
|
+
label: useUiLabel("pruneStale"),
|
|
2101
|
+
prune: async () => {
|
|
2102
|
+
const ids = [...staleIds];
|
|
2103
|
+
await context.removeItemsWithUndo(ids);
|
|
2104
|
+
onPruned?.(ids);
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
// src/hooks/useKeepItemStatusBadge.ts
|
|
2110
|
+
function useKeepItemStatusBadge(status) {
|
|
2111
|
+
return { resolvedStatus: getDisplayStatus2(status), statusLabel: useUiLabel(getStatusLabelKey2(status)) };
|
|
2112
|
+
}
|
|
2113
|
+
function getStatusLabelKey2(status) {
|
|
2114
|
+
switch (status) {
|
|
2115
|
+
case "available":
|
|
2116
|
+
return "statusAvailable";
|
|
2117
|
+
case "expired":
|
|
2118
|
+
return "statusExpired";
|
|
2119
|
+
case "removed":
|
|
2120
|
+
return "statusRemoved";
|
|
2121
|
+
case "deleted":
|
|
2122
|
+
return "statusDeleted";
|
|
2123
|
+
case "private":
|
|
2124
|
+
return "statusPrivate";
|
|
2125
|
+
case "unknown":
|
|
2126
|
+
return "statusUnknown";
|
|
2127
|
+
case "restricted":
|
|
2128
|
+
return "statusPrivate";
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function getDisplayStatus2(status) {
|
|
2132
|
+
if (status === "available") return "available";
|
|
2133
|
+
if (status === "expired") return "expired";
|
|
2134
|
+
if (status === "removed") return "removed";
|
|
2135
|
+
return "restricted";
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/KeepItemStatusBadge.tsx
|
|
2139
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2140
|
+
function KeepItemStatusBadge({ status = "available", label, className, ...props }) {
|
|
2141
|
+
const view = useKeepItemStatusBadge(status);
|
|
2142
|
+
return /* @__PURE__ */ jsx7(
|
|
2143
|
+
"span",
|
|
2144
|
+
{
|
|
2145
|
+
...props,
|
|
2146
|
+
className,
|
|
2147
|
+
"data-keepkit": "status-badge",
|
|
2148
|
+
"data-status": status,
|
|
2149
|
+
"data-item-status": view.resolvedStatus,
|
|
2150
|
+
children: label ?? view.statusLabel
|
|
2151
|
+
}
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
// src/KeepStaleNotice.tsx
|
|
2156
|
+
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2157
|
+
function KeepStaleNotice({
|
|
2158
|
+
item,
|
|
2159
|
+
onRetry,
|
|
2160
|
+
onRemoved,
|
|
2161
|
+
retryLabel,
|
|
2162
|
+
removeLabel,
|
|
2163
|
+
children,
|
|
2164
|
+
className,
|
|
2165
|
+
...props
|
|
2166
|
+
}) {
|
|
2167
|
+
const view = useKeepStaleNotice({ item, onRetry, onRemoved });
|
|
2168
|
+
return /* @__PURE__ */ jsxs4("aside", { ...props, className, "data-keepkit": "stale-notice", "data-state": view.error ? "error" : "stale", children: [
|
|
2169
|
+
/* @__PURE__ */ jsx8(KeepItemStatusBadge, { status: view.status }),
|
|
2170
|
+
children ?? (item.statusReason ? /* @__PURE__ */ jsx8("p", { children: item.statusReason }) : null),
|
|
2171
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
2172
|
+
/* @__PURE__ */ jsx8(
|
|
2173
|
+
"button",
|
|
2174
|
+
{
|
|
2175
|
+
type: "button",
|
|
2176
|
+
"data-keep-action": "retry-item",
|
|
2177
|
+
onClick: () => void view.retry(),
|
|
2178
|
+
disabled: view.isRetrying || view.isMutating,
|
|
2179
|
+
children: retryLabel ?? view.labels.retry
|
|
2180
|
+
}
|
|
2181
|
+
),
|
|
2182
|
+
/* @__PURE__ */ jsx8(
|
|
2183
|
+
"button",
|
|
2184
|
+
{
|
|
2185
|
+
type: "button",
|
|
2186
|
+
"data-keep-action": "remove-item",
|
|
2187
|
+
onClick: () => void view.remove(),
|
|
2188
|
+
disabled: view.isMutating,
|
|
2189
|
+
children: removeLabel ?? view.labels.remove
|
|
2190
|
+
}
|
|
2191
|
+
)
|
|
2192
|
+
] }),
|
|
2193
|
+
view.error ? /* @__PURE__ */ jsx8("p", { role: "alert", children: view.error instanceof Error ? view.error.message : view.labels.error }) : null
|
|
2194
|
+
] });
|
|
2195
|
+
}
|
|
2196
|
+
function KeepPruneStaleButton({
|
|
2197
|
+
statuses = ["expired", "removed", "deleted", "private", "unknown"],
|
|
2198
|
+
label,
|
|
2199
|
+
onPruned,
|
|
2200
|
+
children,
|
|
2201
|
+
disabled,
|
|
2202
|
+
...props
|
|
2203
|
+
}) {
|
|
2204
|
+
const view = useKeepPruneStale({ statuses, onPruned });
|
|
1749
2205
|
return /* @__PURE__ */ jsx8(
|
|
1750
2206
|
"button",
|
|
1751
2207
|
{
|
|
@@ -1753,10 +2209,11 @@ function KeepPruneStaleButton({
|
|
|
1753
2209
|
type: "button",
|
|
1754
2210
|
className: props.className,
|
|
1755
2211
|
"data-keepkit": "prune-stale",
|
|
1756
|
-
"data-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2212
|
+
"data-keep-action": "prune-stale",
|
|
2213
|
+
"data-state": view.staleIds.length > 0 ? "available" : "empty",
|
|
2214
|
+
disabled: view.staleIds.length === 0 || view.isMutating || disabled,
|
|
2215
|
+
onClick: () => void view.prune(),
|
|
2216
|
+
children: children ?? label ?? `${view.label} (${view.staleIds.length})`
|
|
1760
2217
|
}
|
|
1761
2218
|
);
|
|
1762
2219
|
}
|
|
@@ -1792,72 +2249,63 @@ function KeepItemCard({
|
|
|
1792
2249
|
className,
|
|
1793
2250
|
...rootProps
|
|
1794
2251
|
}) {
|
|
1795
|
-
const
|
|
1796
|
-
const savedAtLabel = useUiLabel("saved");
|
|
1797
|
-
const errorLabel = useUiLabel("error");
|
|
1798
|
-
const removeActionLabel = useUiLabel("remove");
|
|
1799
|
-
const itemState = useKeepItem2(item);
|
|
1800
|
-
const contentChildren = asChild && isValidElement2(children) ? void 0 : children;
|
|
1801
|
-
const state = {
|
|
2252
|
+
const view = useKeepItemCard({
|
|
1802
2253
|
item,
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
const
|
|
1813
|
-
const statusLabelKey = item.status && item.status !== "available" ? getStatusLabelKey2(item.status) : "statusUnknown";
|
|
1814
|
-
const unavailableLabel = useUiLabel(statusLabelKey);
|
|
1815
|
-
const tagsLabel = useUiLabel("tags");
|
|
1816
|
-
const statusLabel = item.status && item.status !== "available" ? unavailableLabel : void 0;
|
|
2254
|
+
title,
|
|
2255
|
+
getTitle,
|
|
2256
|
+
getImageProps,
|
|
2257
|
+
href: hrefOption,
|
|
2258
|
+
linkTargetAttribute,
|
|
2259
|
+
linkRel,
|
|
2260
|
+
onRemoveError,
|
|
2261
|
+
onRemoved
|
|
2262
|
+
});
|
|
2263
|
+
const contentChildren = asChild && isValidElement2(children) ? void 0 : children;
|
|
1817
2264
|
function renderLink(content) {
|
|
1818
|
-
if (!href || !isAvailable) return content;
|
|
2265
|
+
if (!view.href || !view.isAvailable) return content;
|
|
1819
2266
|
const linkProps = {
|
|
1820
|
-
href,
|
|
1821
|
-
target:
|
|
1822
|
-
rel:
|
|
2267
|
+
href: view.href,
|
|
2268
|
+
target: view.resolvedLinkTarget,
|
|
2269
|
+
rel: view.resolvedLinkRel,
|
|
1823
2270
|
onClick: (event) => onOpen?.(item, event),
|
|
1824
2271
|
children: content
|
|
1825
2272
|
};
|
|
1826
2273
|
return LinkComponent ? /* @__PURE__ */ jsx9(LinkComponent, { ...linkProps }) : /* @__PURE__ */ jsx9("a", { ...linkProps });
|
|
1827
2274
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
onRemoved?.(item);
|
|
1832
|
-
} catch (error) {
|
|
1833
|
-
onRemoveError?.(error);
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1837
|
-
imageProps ? renderImage?.({ ...imageProps, alt: imageAlt ?? imageProps.alt }, item) ?? (ImageComponent ? /* @__PURE__ */ jsx9(ImageComponent, { ...imageProps, alt: imageAlt ?? imageProps.alt }) : /* @__PURE__ */ jsx9("img", { ...imageProps, alt: imageAlt ?? imageProps.alt })) : null,
|
|
1838
|
-
/* @__PURE__ */ jsx9("h3", { children: linkTarget === "title" ? renderLink(resolvedTitle) : resolvedTitle }),
|
|
2275
|
+
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2276
|
+
view.imageProps ? renderImage?.({ ...view.imageProps, alt: imageAlt ?? view.imageProps.alt }, item) ?? (ImageComponent ? /* @__PURE__ */ jsx9(ImageComponent, { ...view.imageProps, alt: imageAlt ?? view.imageProps.alt }) : /* @__PURE__ */ jsx9("img", { ...view.imageProps, alt: imageAlt ?? view.imageProps.alt })) : null,
|
|
2277
|
+
/* @__PURE__ */ jsx9("h3", { children: linkTarget === "title" ? view.isAvailable ? renderLink(view.resolvedTitle) : view.href ? /* @__PURE__ */ jsx9("span", { "aria-disabled": "true", "data-link-disabled": "true", children: view.resolvedTitle }) : view.resolvedTitle : view.resolvedTitle }),
|
|
1839
2278
|
showSavedAt ? /* @__PURE__ */ jsxs5("div", { "data-card-meta": true, children: [
|
|
1840
2279
|
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1841
|
-
|
|
2280
|
+
view.labels.savedAt,
|
|
1842
2281
|
":"
|
|
1843
2282
|
] }),
|
|
1844
2283
|
" ",
|
|
1845
2284
|
/* @__PURE__ */ jsx9("time", { dateTime: new Date(item.savedAt).toISOString(), children: formatSavedAt(item.savedAt) })
|
|
1846
2285
|
] }) : null,
|
|
1847
|
-
showTags && (item.tags?.length ?? 0) > 0 ? renderTags ? renderTags(item.tags ?? [], item) : /* @__PURE__ */ jsx9("ul", { "aria-label":
|
|
1848
|
-
itemState.error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: getErrorMessage2(itemState.error,
|
|
1849
|
-
statusLabel ? /* @__PURE__ */ jsx9(KeepStaleNotice, { item, onRetry, onRemoved }) : null,
|
|
1850
|
-
showSaveButton && !statusLabel ? /* @__PURE__ */ jsx9(
|
|
2286
|
+
showTags && (item.tags?.length ?? 0) > 0 ? renderTags ? renderTags(item.tags ?? [], item) : /* @__PURE__ */ jsx9("ul", { "aria-label": view.labels.tags, children: item.tags?.map((tag) => /* @__PURE__ */ jsx9("li", { children: tag }, tag)) }) : null,
|
|
2287
|
+
view.itemState.error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: getErrorMessage2(view.itemState.error, view.labels.error) }) : null,
|
|
2288
|
+
view.statusLabel ? /* @__PURE__ */ jsx9(KeepStaleNotice, { item, onRetry, onRemoved }) : null,
|
|
2289
|
+
showSaveButton && !view.statusLabel ? /* @__PURE__ */ jsx9(
|
|
1851
2290
|
KeepButton,
|
|
1852
2291
|
{
|
|
1853
2292
|
item: toKeepButtonItem(item),
|
|
1854
2293
|
labels: saveButtonLabels,
|
|
1855
|
-
getAriaLabel: (buttonState) => `${buttonState.isSaved ?
|
|
2294
|
+
getAriaLabel: (buttonState) => `${buttonState.isSaved ? view.labels.remove : view.labels.save} ${String(view.resolvedTitle)}`
|
|
1856
2295
|
}
|
|
1857
2296
|
) : null,
|
|
1858
|
-
!statusLabel ? /* @__PURE__ */ jsx9(
|
|
2297
|
+
!view.statusLabel ? /* @__PURE__ */ jsx9(
|
|
2298
|
+
"button",
|
|
2299
|
+
{
|
|
2300
|
+
type: "button",
|
|
2301
|
+
"data-keep-action": "remove-item",
|
|
2302
|
+
onClick: () => void view.remove(),
|
|
2303
|
+
disabled: view.itemState.isMutating,
|
|
2304
|
+
children: removeLabel ?? view.labels.remove
|
|
2305
|
+
}
|
|
2306
|
+
) : null
|
|
1859
2307
|
] });
|
|
1860
|
-
const linkedBody = linkTarget === "card" ? renderLink(body) : body;
|
|
2308
|
+
const linkedBody = linkTarget === "card" && view.isAvailable ? renderLink(body) : body;
|
|
1861
2309
|
return renderRoot(
|
|
1862
2310
|
asChild,
|
|
1863
2311
|
isValidElement2(children) ? children : void 0,
|
|
@@ -1865,29 +2313,17 @@ function KeepItemCard({
|
|
|
1865
2313
|
...rootProps,
|
|
1866
2314
|
className,
|
|
1867
2315
|
"data-keepkit": "card",
|
|
1868
|
-
"aria-busy": itemState.isMutating || rootProps["aria-busy"],
|
|
1869
|
-
"
|
|
2316
|
+
"aria-busy": view.itemState.isMutating || rootProps["aria-busy"],
|
|
2317
|
+
"aria-disabled": rootProps["aria-disabled"] ?? (!view.isAvailable ? "true" : void 0),
|
|
2318
|
+
"data-state": view.itemState.error ? "error" : view.itemState.isSaved ? "saved" : "unsaved",
|
|
1870
2319
|
"data-status": item.status ?? "available",
|
|
1871
|
-
"data-
|
|
2320
|
+
"data-item-status": view.displayStatus,
|
|
2321
|
+
"data-loading": view.itemState.isMutating ? "true" : void 0
|
|
1872
2322
|
},
|
|
1873
2323
|
linkedBody,
|
|
1874
2324
|
"KeepItemCard"
|
|
1875
2325
|
);
|
|
1876
2326
|
}
|
|
1877
|
-
function getStatusLabelKey2(status) {
|
|
1878
|
-
switch (status) {
|
|
1879
|
-
case "expired":
|
|
1880
|
-
return "statusExpired";
|
|
1881
|
-
case "removed":
|
|
1882
|
-
return "statusRemoved";
|
|
1883
|
-
case "deleted":
|
|
1884
|
-
return "statusDeleted";
|
|
1885
|
-
case "private":
|
|
1886
|
-
return "statusPrivate";
|
|
1887
|
-
default:
|
|
1888
|
-
return "statusUnknown";
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
2327
|
function formatSavedAt(timestamp) {
|
|
1892
2328
|
return new Date(timestamp).toISOString().slice(0, 10);
|
|
1893
2329
|
}
|
|
@@ -1916,16 +2352,14 @@ function KeepListContent({
|
|
|
1916
2352
|
className,
|
|
1917
2353
|
...rootProps
|
|
1918
2354
|
}) {
|
|
1919
|
-
const
|
|
1920
|
-
const
|
|
1921
|
-
const defaultError = useUiLabel("errorItems");
|
|
1922
|
-
const state = useKeepList2(query);
|
|
2355
|
+
const view = useKeepListView(query);
|
|
2356
|
+
const { state } = view;
|
|
1923
2357
|
const body = getListBody(state, {
|
|
1924
2358
|
children,
|
|
1925
2359
|
renderItem,
|
|
1926
|
-
loading: loading ??
|
|
1927
|
-
empty: empty ??
|
|
1928
|
-
error: errorContent ??
|
|
2360
|
+
loading: loading ?? view.labels.loading,
|
|
2361
|
+
empty: empty ?? view.labels.empty,
|
|
2362
|
+
error: errorContent ?? view.labels.error,
|
|
1929
2363
|
itemCardProps,
|
|
1930
2364
|
layout
|
|
1931
2365
|
});
|
|
@@ -1936,6 +2370,7 @@ function KeepListContent({
|
|
|
1936
2370
|
...rootProps,
|
|
1937
2371
|
className,
|
|
1938
2372
|
"data-keepkit": "list",
|
|
2373
|
+
"data-layout": layout,
|
|
1939
2374
|
"aria-busy": state.isLoading || rootProps["aria-busy"],
|
|
1940
2375
|
"data-state": getListState(state),
|
|
1941
2376
|
"data-loading": state.isLoading ? "true" : void 0
|
|
@@ -1956,21 +2391,45 @@ function getListBody(state, options) {
|
|
|
1956
2391
|
if (state.isHydrated && state.items.length === 0) return resolveContent(options.empty, state);
|
|
1957
2392
|
if (typeof options.children === "function") return options.children(state);
|
|
1958
2393
|
if (options.children !== void 0 && !isValidElement3(options.children)) return options.children;
|
|
1959
|
-
return /* @__PURE__ */ jsx10(
|
|
1960
|
-
"
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2394
|
+
return /* @__PURE__ */ jsx10("ul", { "data-layout": options.layout, children: state.items.map(
|
|
2395
|
+
(item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
|
|
2396
|
+
) });
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// src/KeepTagFilter.tsx
|
|
2400
|
+
import { isValidElement as isValidElement4 } from "react";
|
|
2401
|
+
|
|
2402
|
+
// src/hooks/useKeepTagFilter.ts
|
|
2403
|
+
import { useKeepList as useKeepList4 } from "@keepkit/core/react";
|
|
2404
|
+
import { useCallback, useMemo as useMemo3, useState as useState5 } from "react";
|
|
2405
|
+
function useKeepTagFilter(options) {
|
|
2406
|
+
const { query, controlledValue, defaultValue, onChange, onValueChange } = options;
|
|
2407
|
+
const [uncontrolledValue, setUncontrolledValue] = useState5(defaultValue);
|
|
2408
|
+
const resolvedValue = controlledValue ?? uncontrolledValue;
|
|
2409
|
+
const list = useKeepList4({
|
|
2410
|
+
...query,
|
|
2411
|
+
tags: resolvedValue ? [...query?.tags ?? [], resolvedValue] : query?.tags
|
|
2412
|
+
});
|
|
2413
|
+
const select = useCallback(
|
|
2414
|
+
(tag) => {
|
|
2415
|
+
if (controlledValue === void 0) setUncontrolledValue(tag);
|
|
2416
|
+
onChange?.(tag);
|
|
2417
|
+
onValueChange?.(tag);
|
|
2418
|
+
},
|
|
2419
|
+
[controlledValue, onChange, onValueChange]
|
|
2420
|
+
);
|
|
2421
|
+
const state = useMemo3(
|
|
2422
|
+
() => ({ tags: list.tags, tagCounts: list.tagCounts, value: resolvedValue, select }),
|
|
2423
|
+
[list.tagCounts, list.tags, resolvedValue, select]
|
|
1968
2424
|
);
|
|
2425
|
+
return {
|
|
2426
|
+
state,
|
|
2427
|
+
isLoading: list.isLoading,
|
|
2428
|
+
labels: { all: useUiLabel("allTags"), aria: useUiLabel("filterTags") }
|
|
2429
|
+
};
|
|
1969
2430
|
}
|
|
1970
2431
|
|
|
1971
2432
|
// src/KeepTagFilter.tsx
|
|
1972
|
-
import { useKeepList as useKeepList3 } from "@keepkit/core/react";
|
|
1973
|
-
import { isValidElement as isValidElement4, useCallback, useMemo as useMemo2, useState as useState4 } from "react";
|
|
1974
2433
|
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1975
2434
|
function KeepTagFilter({
|
|
1976
2435
|
query,
|
|
@@ -1987,38 +2446,38 @@ function KeepTagFilter({
|
|
|
1987
2446
|
className,
|
|
1988
2447
|
...rootProps
|
|
1989
2448
|
}) {
|
|
1990
|
-
const
|
|
1991
|
-
const uiAriaLabel = useUiLabel("filterTags");
|
|
1992
|
-
const [uncontrolledValue, setUncontrolledValue] = useState4(defaultValue);
|
|
1993
|
-
const resolvedValue = controlledValue ?? uncontrolledValue;
|
|
1994
|
-
const list = useKeepList3({
|
|
1995
|
-
...query,
|
|
1996
|
-
tags: resolvedValue ? [...query?.tags ?? [], resolvedValue] : query?.tags
|
|
1997
|
-
});
|
|
1998
|
-
const select = useCallback(
|
|
1999
|
-
(tag) => {
|
|
2000
|
-
if (controlledValue === void 0) setUncontrolledValue(tag);
|
|
2001
|
-
onChange?.(tag);
|
|
2002
|
-
onValueChange?.(tag);
|
|
2003
|
-
},
|
|
2004
|
-
[controlledValue, onChange, onValueChange]
|
|
2005
|
-
);
|
|
2006
|
-
const state = useMemo2(
|
|
2007
|
-
() => ({ tags: list.tags, tagCounts: list.tagCounts, value: resolvedValue, select }),
|
|
2008
|
-
[list.tagCounts, list.tags, resolvedValue, select]
|
|
2009
|
-
);
|
|
2449
|
+
const view = useKeepTagFilter({ query, controlledValue, defaultValue, onChange, onValueChange });
|
|
2010
2450
|
const contentChildren = asChild && isValidElement4(children) ? void 0 : children;
|
|
2011
|
-
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs6("fieldset", { children: [
|
|
2012
|
-
/* @__PURE__ */ jsx11("legend", { children: ariaLabel ??
|
|
2013
|
-
/* @__PURE__ */ jsx11(
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
"
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2451
|
+
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? /* @__PURE__ */ jsxs6("fieldset", { children: [
|
|
2452
|
+
/* @__PURE__ */ jsx11("legend", { children: ariaLabel ?? view.labels.aria }),
|
|
2453
|
+
/* @__PURE__ */ jsx11(
|
|
2454
|
+
"button",
|
|
2455
|
+
{
|
|
2456
|
+
type: "button",
|
|
2457
|
+
"data-keep-action": "filter-all-tags",
|
|
2458
|
+
"aria-pressed": view.state.value === void 0,
|
|
2459
|
+
onClick: () => view.state.select(),
|
|
2460
|
+
children: allLabel ?? view.labels.all
|
|
2461
|
+
}
|
|
2462
|
+
),
|
|
2463
|
+
view.state.tags.map((tag) => /* @__PURE__ */ jsxs6(
|
|
2464
|
+
"button",
|
|
2465
|
+
{
|
|
2466
|
+
type: "button",
|
|
2467
|
+
"data-keep-action": "filter-tag",
|
|
2468
|
+
"aria-pressed": view.state.value === tag,
|
|
2469
|
+
onClick: () => view.state.select(tag),
|
|
2470
|
+
children: [
|
|
2471
|
+
renderTag ? renderTag(tag, view.state.tagCounts[tag] ?? 0, view.state.value === tag) : tag,
|
|
2472
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
2473
|
+
" (",
|
|
2474
|
+
view.state.tagCounts[tag] ?? 0,
|
|
2475
|
+
")"
|
|
2476
|
+
] })
|
|
2477
|
+
]
|
|
2478
|
+
},
|
|
2479
|
+
tag
|
|
2480
|
+
))
|
|
2022
2481
|
] });
|
|
2023
2482
|
return renderRoot(
|
|
2024
2483
|
asChild,
|
|
@@ -2027,16 +2486,87 @@ function KeepTagFilter({
|
|
|
2027
2486
|
...rootProps,
|
|
2028
2487
|
className,
|
|
2029
2488
|
"data-keepkit": "tag-filter",
|
|
2030
|
-
"data-state":
|
|
2031
|
-
"data-loading":
|
|
2489
|
+
"data-state": view.state.value === void 0 ? "all" : "filtered",
|
|
2490
|
+
"data-loading": view.isLoading ? "true" : void 0
|
|
2032
2491
|
},
|
|
2033
2492
|
body,
|
|
2034
2493
|
"KeepTagFilter"
|
|
2035
2494
|
);
|
|
2036
2495
|
}
|
|
2037
2496
|
|
|
2497
|
+
// src/hooks/useQueryControls.ts
|
|
2498
|
+
import { useEffect as useEffect2, useState as useState6 } from "react";
|
|
2499
|
+
function useKeepSearchInput(options) {
|
|
2500
|
+
const { controlledValue, defaultValue, debounceMs, onValueChange } = options;
|
|
2501
|
+
const [uncontrolledValue, setUncontrolledValue] = useState6(defaultValue);
|
|
2502
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
2503
|
+
useEffect2(() => {
|
|
2504
|
+
if (!onValueChange) return;
|
|
2505
|
+
if (debounceMs <= 0) {
|
|
2506
|
+
onValueChange(value);
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
const timer = window.setTimeout(() => onValueChange(value), debounceMs);
|
|
2510
|
+
return () => window.clearTimeout(timer);
|
|
2511
|
+
}, [debounceMs, onValueChange, value]);
|
|
2512
|
+
return {
|
|
2513
|
+
value,
|
|
2514
|
+
label: useUiLabel("search"),
|
|
2515
|
+
change: (event) => {
|
|
2516
|
+
if (controlledValue === void 0) setUncontrolledValue(event.currentTarget.value);
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
function useKeepSortSelect(options) {
|
|
2521
|
+
const { controlledValue, defaultValue, onValueChange } = options;
|
|
2522
|
+
const [uncontrolledValue, setUncontrolledValue] = useState6(defaultValue);
|
|
2523
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
2524
|
+
return {
|
|
2525
|
+
value,
|
|
2526
|
+
change: (event) => {
|
|
2527
|
+
const nextValue = event.currentTarget.value;
|
|
2528
|
+
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
2529
|
+
const [by, direction] = nextValue.split(":");
|
|
2530
|
+
onValueChange?.(nextValue, { by, direction });
|
|
2531
|
+
},
|
|
2532
|
+
labels: {
|
|
2533
|
+
sort: useUiLabel("sort"),
|
|
2534
|
+
updatedNewest: useUiLabel("updatedNewest"),
|
|
2535
|
+
updatedOldest: useUiLabel("updatedOldest"),
|
|
2536
|
+
savedNewest: useUiLabel("savedNewest"),
|
|
2537
|
+
savedOldest: useUiLabel("savedOldest")
|
|
2538
|
+
}
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
function useKeepPagination(options) {
|
|
2542
|
+
const { totalCount, pageSize, page, maxPageButtons, onPageChange } = options;
|
|
2543
|
+
const pageCount = Math.max(1, Math.ceil(totalCount / Math.max(1, pageSize)));
|
|
2544
|
+
const currentPage = Math.min(Math.max(1, page), pageCount);
|
|
2545
|
+
const goToPage = (nextPage) => {
|
|
2546
|
+
const next = Math.min(Math.max(1, nextPage), pageCount);
|
|
2547
|
+
onPageChange?.(next, (next - 1) * pageSize);
|
|
2548
|
+
};
|
|
2549
|
+
return {
|
|
2550
|
+
pageCount,
|
|
2551
|
+
currentPage,
|
|
2552
|
+
goToPage,
|
|
2553
|
+
visiblePages: getVisiblePages(currentPage, pageCount, Math.max(1, maxPageButtons)),
|
|
2554
|
+
labels: {
|
|
2555
|
+
previous: useUiLabel("previousPage"),
|
|
2556
|
+
next: useUiLabel("nextPage"),
|
|
2557
|
+
page: useUiLabel("page"),
|
|
2558
|
+
pagination: useUiLabel("pagination")
|
|
2559
|
+
}
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2562
|
+
function getVisiblePages(currentPage, pageCount, maxPageButtons) {
|
|
2563
|
+
if (pageCount <= maxPageButtons) return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
2564
|
+
const half = Math.floor(maxPageButtons / 2);
|
|
2565
|
+
const start = Math.min(Math.max(1, currentPage - half), pageCount - maxPageButtons + 1);
|
|
2566
|
+
return Array.from({ length: maxPageButtons }, (_, index) => start + index);
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2038
2569
|
// src/query-controls.tsx
|
|
2039
|
-
import { useEffect, useState as useState5 } from "react";
|
|
2040
2570
|
import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2041
2571
|
function KeepSearchInput({
|
|
2042
2572
|
value: controlledValue,
|
|
@@ -2047,33 +2577,20 @@ function KeepSearchInput({
|
|
|
2047
2577
|
placeholder,
|
|
2048
2578
|
...props
|
|
2049
2579
|
}) {
|
|
2050
|
-
const
|
|
2051
|
-
const [uncontrolledValue, setUncontrolledValue] = useState5(defaultValue);
|
|
2052
|
-
const value = controlledValue ?? uncontrolledValue;
|
|
2053
|
-
useEffect(() => {
|
|
2054
|
-
if (!onValueChange) return;
|
|
2055
|
-
if (debounceMs <= 0) {
|
|
2056
|
-
onValueChange(value);
|
|
2057
|
-
return;
|
|
2058
|
-
}
|
|
2059
|
-
const timer = window.setTimeout(() => onValueChange(value), debounceMs);
|
|
2060
|
-
return () => window.clearTimeout(timer);
|
|
2061
|
-
}, [debounceMs, onValueChange, value]);
|
|
2580
|
+
const view = useKeepSearchInput({ controlledValue, defaultValue, debounceMs, onValueChange });
|
|
2062
2581
|
return /* @__PURE__ */ jsx12(
|
|
2063
2582
|
"input",
|
|
2064
2583
|
{
|
|
2065
2584
|
...props,
|
|
2066
2585
|
"data-keepkit": "search-input",
|
|
2586
|
+
"data-keep-action": "search",
|
|
2067
2587
|
type: "search",
|
|
2068
|
-
value,
|
|
2069
|
-
"data-state": value ? "active" : "idle",
|
|
2588
|
+
value: view.value,
|
|
2589
|
+
"data-state": view.value ? "active" : "idle",
|
|
2070
2590
|
"data-disabled": props.disabled ? "true" : void 0,
|
|
2071
|
-
"aria-label": ariaLabel ?? label,
|
|
2072
|
-
placeholder: placeholder ?? label,
|
|
2073
|
-
onChange:
|
|
2074
|
-
const nextValue = event.currentTarget.value;
|
|
2075
|
-
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
2076
|
-
}
|
|
2591
|
+
"aria-label": ariaLabel ?? view.label,
|
|
2592
|
+
placeholder: placeholder ?? view.label,
|
|
2593
|
+
onChange: view.change
|
|
2077
2594
|
}
|
|
2078
2595
|
);
|
|
2079
2596
|
}
|
|
@@ -2085,34 +2602,24 @@ function KeepSortSelect({
|
|
|
2085
2602
|
children,
|
|
2086
2603
|
...props
|
|
2087
2604
|
}) {
|
|
2088
|
-
const
|
|
2089
|
-
const updatedNewestLabel = useUiLabel("updatedNewest");
|
|
2090
|
-
const updatedOldestLabel = useUiLabel("updatedOldest");
|
|
2091
|
-
const savedNewestLabel = useUiLabel("savedNewest");
|
|
2092
|
-
const savedOldestLabel = useUiLabel("savedOldest");
|
|
2093
|
-
const [uncontrolledValue, setUncontrolledValue] = useState5(defaultValue);
|
|
2094
|
-
const value = controlledValue ?? uncontrolledValue;
|
|
2605
|
+
const view = useKeepSortSelect({ controlledValue, defaultValue, onValueChange });
|
|
2095
2606
|
const options = children ?? /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2096
|
-
/* @__PURE__ */ jsx12("option", { value: "updatedAt:desc", children:
|
|
2097
|
-
/* @__PURE__ */ jsx12("option", { value: "updatedAt:asc", children:
|
|
2098
|
-
/* @__PURE__ */ jsx12("option", { value: "savedAt:desc", children:
|
|
2099
|
-
/* @__PURE__ */ jsx12("option", { value: "savedAt:asc", children:
|
|
2607
|
+
/* @__PURE__ */ jsx12("option", { value: "updatedAt:desc", children: view.labels.updatedNewest }),
|
|
2608
|
+
/* @__PURE__ */ jsx12("option", { value: "updatedAt:asc", children: view.labels.updatedOldest }),
|
|
2609
|
+
/* @__PURE__ */ jsx12("option", { value: "savedAt:desc", children: view.labels.savedNewest }),
|
|
2610
|
+
/* @__PURE__ */ jsx12("option", { value: "savedAt:asc", children: view.labels.savedOldest })
|
|
2100
2611
|
] });
|
|
2101
2612
|
return /* @__PURE__ */ jsx12(
|
|
2102
2613
|
"select",
|
|
2103
2614
|
{
|
|
2104
2615
|
...props,
|
|
2105
2616
|
"data-keepkit": "sort-select",
|
|
2106
|
-
|
|
2617
|
+
"data-keep-action": "sort",
|
|
2618
|
+
value: view.value,
|
|
2107
2619
|
"data-state": "selected",
|
|
2108
2620
|
"data-disabled": props.disabled ? "true" : void 0,
|
|
2109
|
-
"aria-label": ariaLabel ??
|
|
2110
|
-
onChange:
|
|
2111
|
-
const nextValue = event.currentTarget.value;
|
|
2112
|
-
if (controlledValue === void 0) setUncontrolledValue(nextValue);
|
|
2113
|
-
const [by, direction] = nextValue.split(":");
|
|
2114
|
-
onValueChange?.(nextValue, { by, direction });
|
|
2115
|
-
},
|
|
2621
|
+
"aria-label": ariaLabel ?? view.labels.sort,
|
|
2622
|
+
onChange: view.change,
|
|
2116
2623
|
children: options
|
|
2117
2624
|
}
|
|
2118
2625
|
);
|
|
@@ -2126,128 +2633,50 @@ function KeepPagination({
|
|
|
2126
2633
|
render,
|
|
2127
2634
|
...props
|
|
2128
2635
|
}) {
|
|
2129
|
-
const
|
|
2130
|
-
const nextPageLabel = useUiLabel("nextPage");
|
|
2131
|
-
const pageLabel = useUiLabel("page");
|
|
2132
|
-
const paginationLabel = useUiLabel("pagination");
|
|
2133
|
-
const pageCount = Math.max(1, Math.ceil(totalCount / Math.max(1, pageSize)));
|
|
2134
|
-
const currentPage = Math.min(Math.max(1, page), pageCount);
|
|
2135
|
-
const goToPage = (nextPage) => {
|
|
2136
|
-
const next = Math.min(Math.max(1, nextPage), pageCount);
|
|
2137
|
-
onPageChange?.(next, (next - 1) * pageSize);
|
|
2138
|
-
};
|
|
2636
|
+
const view = useKeepPagination({ totalCount, pageSize, page, maxPageButtons, onPageChange });
|
|
2139
2637
|
const navProps = {
|
|
2140
2638
|
...props,
|
|
2141
2639
|
"data-keepkit": "pagination",
|
|
2142
|
-
"aria-label": props["aria-label"] ??
|
|
2143
|
-
"data-state": pageCount > 1 ? "active" : "idle"
|
|
2640
|
+
"aria-label": props["aria-label"] ?? view.labels.pagination,
|
|
2641
|
+
"data-state": view.pageCount > 1 ? "active" : "idle"
|
|
2144
2642
|
};
|
|
2145
|
-
if (render)
|
|
2146
|
-
|
|
2643
|
+
if (render)
|
|
2644
|
+
return /* @__PURE__ */ jsx12("nav", { ...navProps, children: render({ page: view.currentPage, pageCount: view.pageCount, goToPage: view.goToPage }) });
|
|
2147
2645
|
return /* @__PURE__ */ jsxs7("nav", { ...navProps, children: [
|
|
2148
|
-
/* @__PURE__ */ jsx12(
|
|
2149
|
-
|
|
2646
|
+
/* @__PURE__ */ jsx12(
|
|
2647
|
+
"button",
|
|
2648
|
+
{
|
|
2649
|
+
type: "button",
|
|
2650
|
+
"data-keep-action": "previous-page",
|
|
2651
|
+
onClick: () => view.goToPage(view.currentPage - 1),
|
|
2652
|
+
disabled: view.currentPage <= 1,
|
|
2653
|
+
children: view.labels.previous
|
|
2654
|
+
}
|
|
2655
|
+
),
|
|
2656
|
+
view.visiblePages.map((nextPage) => /* @__PURE__ */ jsx12(
|
|
2150
2657
|
"button",
|
|
2151
2658
|
{
|
|
2152
2659
|
type: "button",
|
|
2153
|
-
"
|
|
2154
|
-
"aria-
|
|
2155
|
-
|
|
2660
|
+
"data-keep-action": "select-page",
|
|
2661
|
+
"aria-current": nextPage === view.currentPage ? "page" : void 0,
|
|
2662
|
+
"aria-label": `${view.labels.page} ${nextPage}`,
|
|
2663
|
+
onClick: () => view.goToPage(nextPage),
|
|
2156
2664
|
children: nextPage
|
|
2157
2665
|
},
|
|
2158
2666
|
nextPage
|
|
2159
2667
|
)),
|
|
2160
|
-
/* @__PURE__ */ jsx12(
|
|
2668
|
+
/* @__PURE__ */ jsx12(
|
|
2669
|
+
"button",
|
|
2670
|
+
{
|
|
2671
|
+
type: "button",
|
|
2672
|
+
"data-keep-action": "next-page",
|
|
2673
|
+
onClick: () => view.goToPage(view.currentPage + 1),
|
|
2674
|
+
disabled: view.currentPage >= view.pageCount,
|
|
2675
|
+
children: view.labels.next
|
|
2676
|
+
}
|
|
2677
|
+
)
|
|
2161
2678
|
] });
|
|
2162
2679
|
}
|
|
2163
|
-
function getVisiblePages(currentPage, pageCount, maxPageButtons) {
|
|
2164
|
-
if (pageCount <= maxPageButtons) return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
2165
|
-
const half = Math.floor(maxPageButtons / 2);
|
|
2166
|
-
const start = Math.min(Math.max(1, currentPage - half), pageCount - maxPageButtons + 1);
|
|
2167
|
-
return Array.from({ length: maxPageButtons }, (_, index) => start + index);
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
// src/url-sync.tsx
|
|
2171
|
-
import {
|
|
2172
|
-
DEFAULT_KEEP_URL_PARAMS,
|
|
2173
|
-
decodeKeepListQuery,
|
|
2174
|
-
encodeKeepListQuery
|
|
2175
|
-
} from "@keepkit/core/core";
|
|
2176
|
-
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
2177
|
-
function createNextPagesRouterAdapter(router) {
|
|
2178
|
-
const getUrl = () => router.asPath ?? (typeof window === "undefined" ? "/" : window.location.href);
|
|
2179
|
-
return {
|
|
2180
|
-
getUrl,
|
|
2181
|
-
subscribe: router.events ? (listener) => {
|
|
2182
|
-
router.events?.on("routeChangeComplete", listener);
|
|
2183
|
-
return () => router.events?.off("routeChangeComplete", listener);
|
|
2184
|
-
} : void 0,
|
|
2185
|
-
navigate: (url, mode) => {
|
|
2186
|
-
void router[mode](url, void 0, { shallow: true });
|
|
2187
|
-
}
|
|
2188
|
-
};
|
|
2189
|
-
}
|
|
2190
|
-
function useKeepUrlSync({
|
|
2191
|
-
enabled = true,
|
|
2192
|
-
query,
|
|
2193
|
-
onQueryChange,
|
|
2194
|
-
options = {},
|
|
2195
|
-
adapter: providedAdapter
|
|
2196
|
-
}) {
|
|
2197
|
-
const browserAdapterRef = useRef2(getBrowserAdapter());
|
|
2198
|
-
const adapter = providedAdapter ?? browserAdapterRef.current;
|
|
2199
|
-
const onQueryChangeRef = useRef2(onQueryChange);
|
|
2200
|
-
onQueryChangeRef.current = onQueryChange;
|
|
2201
|
-
const skipWriteRef = useRef2(true);
|
|
2202
|
-
const params = options.params;
|
|
2203
|
-
useEffect2(() => {
|
|
2204
|
-
if (!enabled) return;
|
|
2205
|
-
const read = () => {
|
|
2206
|
-
const url = adapter.getUrl();
|
|
2207
|
-
const decoded = decodeKeepListQuery(url, { params });
|
|
2208
|
-
skipWriteRef.current = true;
|
|
2209
|
-
onQueryChangeRef.current((previousQuery) => ({
|
|
2210
|
-
...previousQuery,
|
|
2211
|
-
...decoded.search ? { search: decoded.search } : { search: void 0 },
|
|
2212
|
-
...decoded.tags ? { tags: decoded.tags } : { tags: void 0 },
|
|
2213
|
-
...decoded.sort ? { sort: decoded.sort } : {},
|
|
2214
|
-
...decoded.pagination ? { pagination: { ...previousQuery.pagination, ...decoded.pagination } } : {}
|
|
2215
|
-
}));
|
|
2216
|
-
};
|
|
2217
|
-
read();
|
|
2218
|
-
return adapter.subscribe?.(read);
|
|
2219
|
-
}, [adapter, enabled, params]);
|
|
2220
|
-
useEffect2(() => {
|
|
2221
|
-
if (!enabled) return;
|
|
2222
|
-
if (skipWriteRef.current) {
|
|
2223
|
-
skipWriteRef.current = false;
|
|
2224
|
-
return;
|
|
2225
|
-
}
|
|
2226
|
-
const currentUrl = new URL(adapter.getUrl(), "http://keepkit.invalid");
|
|
2227
|
-
const urlParams = { ...DEFAULT_KEEP_URL_PARAMS, ...params };
|
|
2228
|
-
for (const key of Object.values(urlParams)) currentUrl.searchParams.delete(key);
|
|
2229
|
-
const nextParams = encodeKeepListQuery(query, { params });
|
|
2230
|
-
nextParams.forEach((value, key) => {
|
|
2231
|
-
currentUrl.searchParams.append(key, value);
|
|
2232
|
-
});
|
|
2233
|
-
const nextUrl = `${currentUrl.pathname}${currentUrl.search}${currentUrl.hash}`;
|
|
2234
|
-
adapter.navigate(nextUrl, options.history ?? "push");
|
|
2235
|
-
}, [adapter, enabled, options.history, params, query]);
|
|
2236
|
-
}
|
|
2237
|
-
function getBrowserAdapter() {
|
|
2238
|
-
return {
|
|
2239
|
-
getUrl: () => typeof window === "undefined" ? "/" : window.location.href,
|
|
2240
|
-
subscribe: (listener) => {
|
|
2241
|
-
if (typeof window === "undefined") return () => void 0;
|
|
2242
|
-
window.addEventListener("popstate", listener);
|
|
2243
|
-
return () => window.removeEventListener("popstate", listener);
|
|
2244
|
-
},
|
|
2245
|
-
navigate: (url, mode) => {
|
|
2246
|
-
if (typeof window === "undefined") return;
|
|
2247
|
-
window.history[mode === "push" ? "pushState" : "replaceState"]({}, "", url);
|
|
2248
|
-
}
|
|
2249
|
-
};
|
|
2250
|
-
}
|
|
2251
2680
|
|
|
2252
2681
|
// src/KeepCollection.tsx
|
|
2253
2682
|
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -2272,90 +2701,27 @@ function KeepCollectionContent({
|
|
|
2272
2701
|
className,
|
|
2273
2702
|
...rootProps
|
|
2274
2703
|
}) {
|
|
2275
|
-
const
|
|
2276
|
-
search: true,
|
|
2277
|
-
sort: true,
|
|
2278
|
-
pagination: true,
|
|
2279
|
-
tagFilter: false,
|
|
2280
|
-
bulkActions: false,
|
|
2281
|
-
...features
|
|
2282
|
-
};
|
|
2283
|
-
const [searchValue, setSearchValue] = useState6(query.search?.query ?? "");
|
|
2284
|
-
const [sort, setSort] = useState6(query.sort ?? { by: "updatedAt", direction: "desc" });
|
|
2285
|
-
const [tag, setTag] = useState6(query.tags?.[0]);
|
|
2286
|
-
const [page, setPage] = useState6(query.pagination?.page ?? 1);
|
|
2287
|
-
const resolvedPageSize = query.pagination?.pageSize ?? pageSize;
|
|
2288
|
-
const resolvedQuery = useMemo3(
|
|
2289
|
-
() => ({
|
|
2290
|
-
...query,
|
|
2291
|
-
search: enabled.search ? { ...query.search, query: searchValue } : query.search,
|
|
2292
|
-
sort: enabled.sort ? sort : query.sort,
|
|
2293
|
-
tags: tag ? [.../* @__PURE__ */ new Set([...query.tags ?? [], tag])] : query.tags,
|
|
2294
|
-
pagination: enabled.pagination ? { ...query.pagination, page, pageSize: resolvedPageSize } : query.pagination
|
|
2295
|
-
}),
|
|
2296
|
-
[enabled.pagination, enabled.search, enabled.sort, page, query, resolvedPageSize, searchValue, sort, tag]
|
|
2297
|
-
);
|
|
2298
|
-
useKeepUrlSync({
|
|
2299
|
-
enabled: Boolean(urlSync),
|
|
2300
|
-
query: resolvedQuery,
|
|
2301
|
-
onQueryChange: (nextOrUpdater) => {
|
|
2302
|
-
const next = typeof nextOrUpdater === "function" ? nextOrUpdater(resolvedQuery) : nextOrUpdater;
|
|
2303
|
-
setSearchValue(next.search?.query ?? "");
|
|
2304
|
-
setSort(next.sort ?? { by: "updatedAt", direction: "desc" });
|
|
2305
|
-
setTag(next.tags?.[0]);
|
|
2306
|
-
setPage(next.pagination?.page ?? 1);
|
|
2307
|
-
},
|
|
2308
|
-
options: typeof urlSync === "object" ? urlSync : {},
|
|
2309
|
-
adapter: urlAdapter
|
|
2310
|
-
});
|
|
2311
|
-
const list = useKeepList4(resolvedQuery);
|
|
2704
|
+
const view = useKeepCollection({ query, pageSize, urlSync, urlAdapter, features });
|
|
2312
2705
|
return /* @__PURE__ */ jsxs8(
|
|
2313
2706
|
"section",
|
|
2314
2707
|
{
|
|
2315
2708
|
...rootProps,
|
|
2316
2709
|
className,
|
|
2317
2710
|
"data-keepkit": "collection",
|
|
2318
|
-
"
|
|
2319
|
-
"
|
|
2320
|
-
"data-
|
|
2711
|
+
"data-layout": layout,
|
|
2712
|
+
"aria-busy": view.list.isLoading || view.list.isMutating || rootProps["aria-busy"],
|
|
2713
|
+
"data-state": getCollectionState(view.list),
|
|
2714
|
+
"data-loading": view.list.isLoading || view.list.isMutating ? "true" : void 0,
|
|
2321
2715
|
children: [
|
|
2322
2716
|
/* @__PURE__ */ jsxs8("div", { children: [
|
|
2323
|
-
enabled.search ? /* @__PURE__ */ jsx13(
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
value: searchValue,
|
|
2327
|
-
onValueChange: (value) => {
|
|
2328
|
-
setSearchValue(value);
|
|
2329
|
-
setPage(1);
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
) : null,
|
|
2333
|
-
enabled.sort ? /* @__PURE__ */ jsx13(
|
|
2334
|
-
KeepSortSelect,
|
|
2335
|
-
{
|
|
2336
|
-
value: sortToValue(sort),
|
|
2337
|
-
onValueChange: (_value, nextSort) => {
|
|
2338
|
-
setSort(nextSort);
|
|
2339
|
-
setPage(1);
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2342
|
-
) : null,
|
|
2343
|
-
enabled.tagFilter ? /* @__PURE__ */ jsx13(
|
|
2344
|
-
KeepTagFilter,
|
|
2345
|
-
{
|
|
2346
|
-
query,
|
|
2347
|
-
value: tag,
|
|
2348
|
-
onValueChange: (value) => {
|
|
2349
|
-
setTag(value);
|
|
2350
|
-
setPage(1);
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
) : null
|
|
2717
|
+
view.enabled.search ? /* @__PURE__ */ jsx13(KeepSearchInput, { value: view.searchValue, onValueChange: view.setSearchValue }) : null,
|
|
2718
|
+
view.enabled.sort ? /* @__PURE__ */ jsx13(KeepSortSelect, { value: view.sortValue, onValueChange: view.setSortValue }) : null,
|
|
2719
|
+
view.enabled.tagFilter ? /* @__PURE__ */ jsx13(KeepTagFilter, { query, value: view.tag, onValueChange: view.setTag }) : null
|
|
2354
2720
|
] }),
|
|
2355
2721
|
/* @__PURE__ */ jsx13(
|
|
2356
2722
|
KeepList,
|
|
2357
2723
|
{
|
|
2358
|
-
query: resolvedQuery,
|
|
2724
|
+
query: view.resolvedQuery,
|
|
2359
2725
|
renderItem,
|
|
2360
2726
|
itemCardProps,
|
|
2361
2727
|
layout,
|
|
@@ -2364,16 +2730,16 @@ function KeepCollectionContent({
|
|
|
2364
2730
|
error
|
|
2365
2731
|
}
|
|
2366
2732
|
),
|
|
2367
|
-
enabled.pagination ? /* @__PURE__ */ jsx13(
|
|
2733
|
+
view.enabled.pagination ? /* @__PURE__ */ jsx13(
|
|
2368
2734
|
KeepPagination,
|
|
2369
2735
|
{
|
|
2370
|
-
totalCount: list.totalCount,
|
|
2371
|
-
pageSize: resolvedPageSize,
|
|
2372
|
-
page: list.page,
|
|
2373
|
-
onPageChange:
|
|
2736
|
+
totalCount: view.list.totalCount,
|
|
2737
|
+
pageSize: view.resolvedPageSize,
|
|
2738
|
+
page: view.list.page,
|
|
2739
|
+
onPageChange: view.setPage
|
|
2374
2740
|
}
|
|
2375
2741
|
) : null,
|
|
2376
|
-
enabled.bulkActions ? /* @__PURE__ */ jsx13(KeepBulkActions, { query: resolvedQuery }) : null
|
|
2742
|
+
view.enabled.bulkActions ? /* @__PURE__ */ jsx13(KeepBulkActions, { query: view.resolvedQuery }) : null
|
|
2377
2743
|
]
|
|
2378
2744
|
}
|
|
2379
2745
|
);
|
|
@@ -2387,41 +2753,19 @@ function getCollectionState(list) {
|
|
|
2387
2753
|
|
|
2388
2754
|
// src/KeepLayout.tsx
|
|
2389
2755
|
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
2390
|
-
function KeepLayout({ layout = "list", children,
|
|
2391
|
-
|
|
2392
|
-
return /* @__PURE__ */ jsx14("div", { ...props, style: { ...layoutStyle, ...style }, "data-keepkit": "layout", "data-layout": layout, children });
|
|
2756
|
+
function KeepLayout({ layout = "list", children, ...props }) {
|
|
2757
|
+
return /* @__PURE__ */ jsx14("div", { ...props, "data-keepkit": "layout", "data-layout": layout, children });
|
|
2393
2758
|
}
|
|
2394
2759
|
|
|
2395
2760
|
// src/KeepNoteEditor.tsx
|
|
2761
|
+
import { isValidElement as isValidElement5 } from "react";
|
|
2762
|
+
|
|
2763
|
+
// src/hooks/useKeepNoteEditor.ts
|
|
2396
2764
|
import { useKeepItem as useKeepItem3 } from "@keepkit/core/react";
|
|
2397
|
-
import {
|
|
2398
|
-
|
|
2399
|
-
useCallback as useCallback2,
|
|
2400
|
-
useEffect as useEffect3,
|
|
2401
|
-
useRef as useRef3,
|
|
2402
|
-
useState as useState7
|
|
2403
|
-
} from "react";
|
|
2404
|
-
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2405
|
-
function KeepNoteEditor({
|
|
2406
|
-
item,
|
|
2407
|
-
label,
|
|
2408
|
-
saveLabel,
|
|
2409
|
-
placeholder,
|
|
2410
|
-
debounceMs = 300,
|
|
2411
|
-
onSaved,
|
|
2412
|
-
onSaveError,
|
|
2413
|
-
render,
|
|
2414
|
-
children,
|
|
2415
|
-
asChild = false,
|
|
2416
|
-
className,
|
|
2417
|
-
...formProps
|
|
2418
|
-
}) {
|
|
2419
|
-
const defaultLabel = useUiLabel("note");
|
|
2420
|
-
const defaultSaveLabel = useUiLabel("saveNote");
|
|
2421
|
-
const errorLabel = useUiLabel("error");
|
|
2765
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState7 } from "react";
|
|
2766
|
+
function useKeepNoteEditor({ item, debounceMs, onSaved, onSaveError }) {
|
|
2422
2767
|
const itemState = useKeepItem3(item);
|
|
2423
2768
|
const { error, isMutating, item: savedItem, updateNote } = itemState;
|
|
2424
|
-
const contentChildren = asChild && isValidElement5(children) ? void 0 : children;
|
|
2425
2769
|
const [note, setNote] = useState7(item.note ?? "");
|
|
2426
2770
|
const baselineNote = savedItem?.note ?? item.note ?? "";
|
|
2427
2771
|
const isDirty = note !== baselineNote;
|
|
@@ -2433,9 +2777,9 @@ function KeepNoteEditor({
|
|
|
2433
2777
|
await updateNote(nextNote);
|
|
2434
2778
|
lastSavedNoteRef.current = note;
|
|
2435
2779
|
onSaved?.(nextNote);
|
|
2436
|
-
} catch (
|
|
2437
|
-
onSaveError?.(
|
|
2438
|
-
throw
|
|
2780
|
+
} catch (cause) {
|
|
2781
|
+
onSaveError?.(cause);
|
|
2782
|
+
throw cause;
|
|
2439
2783
|
}
|
|
2440
2784
|
}, [note, onSaveError, onSaved, updateNote]);
|
|
2441
2785
|
useEffect3(() => {
|
|
@@ -2452,31 +2796,62 @@ function KeepNoteEditor({
|
|
|
2452
2796
|
error,
|
|
2453
2797
|
save
|
|
2454
2798
|
};
|
|
2455
|
-
const
|
|
2799
|
+
const submit = (event) => {
|
|
2800
|
+
event.preventDefault();
|
|
2801
|
+
void save().catch(() => void 0);
|
|
2802
|
+
};
|
|
2803
|
+
return {
|
|
2804
|
+
state,
|
|
2805
|
+
submit,
|
|
2806
|
+
handleKeyDown: (event) => {
|
|
2807
|
+
if (event.key !== "Enter" || !event.ctrlKey && !event.metaKey) return;
|
|
2808
|
+
event.preventDefault();
|
|
2809
|
+
void save().catch(() => void 0);
|
|
2810
|
+
},
|
|
2811
|
+
labels: {
|
|
2812
|
+
note: useUiLabel("note"),
|
|
2813
|
+
save: useUiLabel("saveNote"),
|
|
2814
|
+
error: useUiLabel("error")
|
|
2815
|
+
}
|
|
2816
|
+
};
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
// src/KeepNoteEditor.tsx
|
|
2820
|
+
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2821
|
+
function KeepNoteEditor({
|
|
2822
|
+
item,
|
|
2823
|
+
label,
|
|
2824
|
+
saveLabel,
|
|
2825
|
+
placeholder,
|
|
2826
|
+
debounceMs = 300,
|
|
2827
|
+
onSaved,
|
|
2828
|
+
onSaveError,
|
|
2829
|
+
render,
|
|
2830
|
+
children,
|
|
2831
|
+
asChild = false,
|
|
2832
|
+
className,
|
|
2833
|
+
...formProps
|
|
2834
|
+
}) {
|
|
2835
|
+
const view = useKeepNoteEditor({ item, debounceMs, onSaved, onSaveError });
|
|
2836
|
+
const { error, isDirty, isSaving, note, setNote } = view.state;
|
|
2837
|
+
const contentChildren = asChild && isValidElement5(children) ? void 0 : children;
|
|
2838
|
+
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? /* @__PURE__ */ jsxs9(Fragment5, { children: [
|
|
2456
2839
|
/* @__PURE__ */ jsxs9("label", { children: [
|
|
2457
|
-
label ??
|
|
2840
|
+
label ?? view.labels.note,
|
|
2458
2841
|
/* @__PURE__ */ jsx15(
|
|
2459
2842
|
"textarea",
|
|
2460
2843
|
{
|
|
2844
|
+
"data-keep-action": "edit-note",
|
|
2461
2845
|
value: note,
|
|
2462
2846
|
onChange: (event) => setNote(event.currentTarget.value),
|
|
2463
2847
|
placeholder,
|
|
2464
|
-
disabled:
|
|
2465
|
-
onKeyDown:
|
|
2466
|
-
if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
|
|
2467
|
-
event.preventDefault();
|
|
2468
|
-
void save().catch(() => void 0);
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2848
|
+
disabled: isSaving,
|
|
2849
|
+
onKeyDown: view.handleKeyDown
|
|
2471
2850
|
}
|
|
2472
2851
|
)
|
|
2473
2852
|
] }),
|
|
2474
|
-
/* @__PURE__ */ jsx15("button", { type: "submit", disabled:
|
|
2853
|
+
/* @__PURE__ */ jsx15("button", { type: "submit", "data-keep-action": "save-note", disabled: isSaving, "aria-busy": isSaving, children: saveLabel ?? view.labels.save })
|
|
2475
2854
|
] });
|
|
2476
|
-
const handleSubmit = (event) => {
|
|
2477
|
-
event.preventDefault();
|
|
2478
|
-
void save().catch(() => void 0);
|
|
2479
|
-
};
|
|
2480
2855
|
if (!asChild) {
|
|
2481
2856
|
return /* @__PURE__ */ jsxs9(
|
|
2482
2857
|
"form",
|
|
@@ -2484,14 +2859,14 @@ function KeepNoteEditor({
|
|
|
2484
2859
|
...formProps,
|
|
2485
2860
|
className,
|
|
2486
2861
|
"data-keepkit": "note-editor",
|
|
2487
|
-
onSubmit:
|
|
2488
|
-
"aria-busy":
|
|
2862
|
+
onSubmit: view.submit,
|
|
2863
|
+
"aria-busy": isSaving || formProps["aria-busy"],
|
|
2489
2864
|
"data-state": error ? "error" : isDirty ? "dirty" : "clean",
|
|
2490
|
-
"data-loading":
|
|
2491
|
-
"data-disabled":
|
|
2865
|
+
"data-loading": isSaving ? "true" : void 0,
|
|
2866
|
+
"data-disabled": isSaving ? "true" : void 0,
|
|
2492
2867
|
children: [
|
|
2493
2868
|
body,
|
|
2494
|
-
error ? /* @__PURE__ */ jsx15("p", { role: "alert", children: getErrorMessage3(error,
|
|
2869
|
+
error ? /* @__PURE__ */ jsx15("p", { role: "alert", children: getErrorMessage3(error, view.labels.error) }) : null
|
|
2495
2870
|
]
|
|
2496
2871
|
}
|
|
2497
2872
|
);
|
|
@@ -2503,11 +2878,11 @@ function KeepNoteEditor({
|
|
|
2503
2878
|
...formProps,
|
|
2504
2879
|
className,
|
|
2505
2880
|
"data-keepkit": "note-editor",
|
|
2506
|
-
onSubmit:
|
|
2507
|
-
"aria-busy":
|
|
2881
|
+
onSubmit: view.submit,
|
|
2882
|
+
"aria-busy": isSaving || formProps["aria-busy"],
|
|
2508
2883
|
"data-state": error ? "error" : isDirty ? "dirty" : "clean",
|
|
2509
|
-
"data-loading":
|
|
2510
|
-
"data-disabled":
|
|
2884
|
+
"data-loading": isSaving ? "true" : void 0,
|
|
2885
|
+
"data-disabled": isSaving ? "true" : void 0
|
|
2511
2886
|
},
|
|
2512
2887
|
body,
|
|
2513
2888
|
"KeepNoteEditor"
|
|
@@ -2517,9 +2892,62 @@ function getErrorMessage3(error, fallback) {
|
|
|
2517
2892
|
return error instanceof Error ? error.message : fallback;
|
|
2518
2893
|
}
|
|
2519
2894
|
|
|
2520
|
-
// src/
|
|
2895
|
+
// src/hooks/useKeepSyncRecoveryDialog.ts
|
|
2521
2896
|
import { useKeepContext as useKeepContext3 } from "@keepkit/core/react";
|
|
2522
2897
|
import { useEffect as useEffect4, useState as useState8 } from "react";
|
|
2898
|
+
function useKeepSyncRecoveryDialog(options) {
|
|
2899
|
+
const { open, onOpenChange, conflicts, onManualMerge } = options;
|
|
2900
|
+
const context = useKeepContext3();
|
|
2901
|
+
const conflictList = conflicts ?? context.syncState.conflicts ?? [];
|
|
2902
|
+
const hasRecovery = conflictList.length > 0 || context.syncState.status === "error" || Boolean(context.error);
|
|
2903
|
+
const [dismissed, setDismissed] = useState8(false);
|
|
2904
|
+
const [busyId, setBusyId] = useState8();
|
|
2905
|
+
const [error, setError] = useState8();
|
|
2906
|
+
useEffect4(() => {
|
|
2907
|
+
if (hasRecovery) setDismissed(false);
|
|
2908
|
+
}, [hasRecovery]);
|
|
2909
|
+
return {
|
|
2910
|
+
conflictList,
|
|
2911
|
+
isOpen: open ?? (hasRecovery && !dismissed),
|
|
2912
|
+
busyId,
|
|
2913
|
+
error,
|
|
2914
|
+
showBackupRecovery: context.syncState.status === "error" || Boolean(context.error),
|
|
2915
|
+
close: () => {
|
|
2916
|
+
setDismissed(true);
|
|
2917
|
+
onOpenChange?.(false);
|
|
2918
|
+
},
|
|
2919
|
+
resolve: async (conflict, resolution) => {
|
|
2920
|
+
setError(void 0);
|
|
2921
|
+
setBusyId(conflict.id);
|
|
2922
|
+
try {
|
|
2923
|
+
const merged = resolution === "manual" ? await onManualMerge?.(conflict) : void 0;
|
|
2924
|
+
if (resolution === "manual" && !merged) throw new Error("A manual merge result is required.");
|
|
2925
|
+
await context.resolveSyncConflict(conflict.id, resolution, merged);
|
|
2926
|
+
} catch (cause) {
|
|
2927
|
+
setError(cause);
|
|
2928
|
+
} finally {
|
|
2929
|
+
setBusyId(void 0);
|
|
2930
|
+
}
|
|
2931
|
+
},
|
|
2932
|
+
labels: {
|
|
2933
|
+
close: useUiLabel("close"),
|
|
2934
|
+
title: useUiLabel("resolveSync"),
|
|
2935
|
+
conflict: useUiLabel("syncConflict"),
|
|
2936
|
+
keepLocal: useUiLabel("keepLocal"),
|
|
2937
|
+
useServer: useUiLabel("useServer"),
|
|
2938
|
+
manualMerge: useUiLabel("manualMerge"),
|
|
2939
|
+
localVersion: useUiLabel("localVersion"),
|
|
2940
|
+
remoteVersion: useUiLabel("remoteVersion"),
|
|
2941
|
+
updatedAt: useUiLabel("updatedAt"),
|
|
2942
|
+
note: useUiLabel("note"),
|
|
2943
|
+
backupRecovery: useUiLabel("backupRecovery"),
|
|
2944
|
+
backupRecoveryDescription: useUiLabel("backupRecoveryDescription"),
|
|
2945
|
+
error: useUiLabel("error")
|
|
2946
|
+
}
|
|
2947
|
+
};
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
// src/KeepSyncRecoveryDialog.tsx
|
|
2523
2951
|
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2524
2952
|
function KeepSyncRecoveryDialog({
|
|
2525
2953
|
open,
|
|
@@ -2533,44 +2961,8 @@ function KeepSyncRecoveryDialog({
|
|
|
2533
2961
|
className,
|
|
2534
2962
|
...props
|
|
2535
2963
|
}) {
|
|
2536
|
-
const
|
|
2537
|
-
|
|
2538
|
-
const defaultDialogTitle = useUiLabel("resolveSync");
|
|
2539
|
-
const dialogTitle = title ?? defaultDialogTitle;
|
|
2540
|
-
const conflictLabel = useUiLabel("syncConflict");
|
|
2541
|
-
const keepLocalLabel = useUiLabel("keepLocal");
|
|
2542
|
-
const useServerLabel = useUiLabel("useServer");
|
|
2543
|
-
const manualMergeLabel = useUiLabel("manualMerge");
|
|
2544
|
-
const backupRecoveryLabel = useUiLabel("backupRecovery");
|
|
2545
|
-
const backupRecoveryDescription = useUiLabel("backupRecoveryDescription");
|
|
2546
|
-
const errorLabel = useUiLabel("error");
|
|
2547
|
-
const conflictList = conflicts ?? context.syncState.conflicts ?? [];
|
|
2548
|
-
const hasRecovery = conflictList.length > 0 || context.syncState.status === "error" || Boolean(context.error);
|
|
2549
|
-
const [dismissed, setDismissed] = useState8(false);
|
|
2550
|
-
const [busyId, setBusyId] = useState8();
|
|
2551
|
-
const [error, setError] = useState8();
|
|
2552
|
-
useEffect4(() => {
|
|
2553
|
-
if (hasRecovery) setDismissed(false);
|
|
2554
|
-
}, [hasRecovery]);
|
|
2555
|
-
const isOpen = open ?? (hasRecovery && !dismissed);
|
|
2556
|
-
if (!isOpen) return null;
|
|
2557
|
-
const close = () => {
|
|
2558
|
-
setDismissed(true);
|
|
2559
|
-
onOpenChange?.(false);
|
|
2560
|
-
};
|
|
2561
|
-
async function resolve(conflict, resolution) {
|
|
2562
|
-
setError(void 0);
|
|
2563
|
-
setBusyId(conflict.id);
|
|
2564
|
-
try {
|
|
2565
|
-
const merged = resolution === "manual" ? await onManualMerge?.(conflict) : void 0;
|
|
2566
|
-
if (resolution === "manual" && !merged) throw new Error("A manual merge result is required.");
|
|
2567
|
-
await context.resolveSyncConflict(conflict.id, resolution, merged);
|
|
2568
|
-
} catch (cause) {
|
|
2569
|
-
setError(cause);
|
|
2570
|
-
} finally {
|
|
2571
|
-
setBusyId(void 0);
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2964
|
+
const view = useKeepSyncRecoveryDialog({ open, onOpenChange, conflicts, onManualMerge });
|
|
2965
|
+
if (!view.isOpen) return null;
|
|
2574
2966
|
return /* @__PURE__ */ jsxs10(
|
|
2575
2967
|
"section",
|
|
2576
2968
|
{
|
|
@@ -2579,55 +2971,115 @@ function KeepSyncRecoveryDialog({
|
|
|
2579
2971
|
role: "dialog",
|
|
2580
2972
|
"aria-modal": "true",
|
|
2581
2973
|
"aria-labelledby": "keepkit-sync-recovery-title",
|
|
2974
|
+
"aria-describedby": view.error ? "keepkit-sync-recovery-error" : void 0,
|
|
2975
|
+
"aria-busy": view.busyId !== void 0,
|
|
2582
2976
|
"data-keepkit": "sync-recovery",
|
|
2583
|
-
"data-state": conflictList.length > 0 ? "conflict" : "error",
|
|
2584
|
-
"data-loading": busyId !== void 0 ? "true" : void 0,
|
|
2977
|
+
"data-state": view.conflictList.length > 0 ? "conflict" : "error",
|
|
2978
|
+
"data-loading": view.busyId !== void 0 ? "true" : void 0,
|
|
2585
2979
|
children: [
|
|
2586
2980
|
/* @__PURE__ */ jsxs10("header", { children: [
|
|
2587
|
-
/* @__PURE__ */ jsx16("h2", { id: "keepkit-sync-recovery-title", children:
|
|
2588
|
-
/* @__PURE__ */ jsx16("button", { type: "button", onClick: close, "aria-label":
|
|
2981
|
+
/* @__PURE__ */ jsx16("h2", { id: "keepkit-sync-recovery-title", children: title ?? view.labels.title }),
|
|
2982
|
+
/* @__PURE__ */ jsx16("button", { type: "button", "data-keep-action": "close-dialog", onClick: view.close, "aria-label": view.labels.close, children: view.labels.close })
|
|
2589
2983
|
] }),
|
|
2590
2984
|
children,
|
|
2591
|
-
conflictList.length > 0 ? /* @__PURE__ */ jsxs10("div", { children: [
|
|
2592
|
-
/* @__PURE__ */ jsx16("p", { children:
|
|
2593
|
-
conflictList.map((conflict) => /* @__PURE__ */ jsxs10("article", { "data-conflict-id": conflict.id, children: [
|
|
2985
|
+
view.conflictList.length > 0 ? /* @__PURE__ */ jsxs10("div", { children: [
|
|
2986
|
+
/* @__PURE__ */ jsx16("p", { children: view.labels.conflict }),
|
|
2987
|
+
view.conflictList.map((conflict) => /* @__PURE__ */ jsxs10("article", { "data-conflict-id": conflict.id, children: [
|
|
2594
2988
|
/* @__PURE__ */ jsx16("h3", { children: getMetaTitle(conflict.operation.item?.meta) ?? conflict.id }),
|
|
2989
|
+
/* @__PURE__ */ jsxs10("div", { "data-conflict-preview": true, children: [
|
|
2990
|
+
/* @__PURE__ */ jsx16(
|
|
2991
|
+
ConflictPreview,
|
|
2992
|
+
{
|
|
2993
|
+
item: conflict.operation.item,
|
|
2994
|
+
heading: view.labels.localVersion,
|
|
2995
|
+
updatedAtLabel: view.labels.updatedAt,
|
|
2996
|
+
noteLabel: view.labels.note,
|
|
2997
|
+
side: "local"
|
|
2998
|
+
}
|
|
2999
|
+
),
|
|
3000
|
+
/* @__PURE__ */ jsx16(
|
|
3001
|
+
ConflictPreview,
|
|
3002
|
+
{
|
|
3003
|
+
item: conflict.remote,
|
|
3004
|
+
heading: view.labels.remoteVersion,
|
|
3005
|
+
updatedAtLabel: view.labels.updatedAt,
|
|
3006
|
+
noteLabel: view.labels.note,
|
|
3007
|
+
side: "remote"
|
|
3008
|
+
}
|
|
3009
|
+
)
|
|
3010
|
+
] }),
|
|
2595
3011
|
/* @__PURE__ */ jsxs10("div", { children: [
|
|
2596
|
-
/* @__PURE__ */ jsx16("button", { type: "button", onClick: () => void resolve(conflict, "local"), disabled: busyId !== void 0, children: keepLocalLabel }),
|
|
2597
|
-
/* @__PURE__ */ jsx16("button", { type: "button", onClick: () => void resolve(conflict, "remote"), disabled: busyId !== void 0, children: useServerLabel }),
|
|
2598
3012
|
/* @__PURE__ */ jsx16(
|
|
2599
3013
|
"button",
|
|
2600
3014
|
{
|
|
2601
3015
|
type: "button",
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
3016
|
+
"data-keep-action": "keep-local",
|
|
3017
|
+
onClick: () => void view.resolve(conflict, "local"),
|
|
3018
|
+
disabled: view.busyId !== void 0,
|
|
3019
|
+
children: view.labels.keepLocal
|
|
3020
|
+
}
|
|
3021
|
+
),
|
|
3022
|
+
/* @__PURE__ */ jsx16(
|
|
3023
|
+
"button",
|
|
3024
|
+
{
|
|
3025
|
+
type: "button",
|
|
3026
|
+
"data-keep-action": "use-server",
|
|
3027
|
+
onClick: () => void view.resolve(conflict, "remote"),
|
|
3028
|
+
disabled: view.busyId !== void 0,
|
|
3029
|
+
children: view.labels.useServer
|
|
3030
|
+
}
|
|
3031
|
+
),
|
|
3032
|
+
/* @__PURE__ */ jsx16(
|
|
3033
|
+
"button",
|
|
3034
|
+
{
|
|
3035
|
+
type: "button",
|
|
3036
|
+
"data-keep-action": "manual-merge",
|
|
3037
|
+
onClick: () => void view.resolve(conflict, "manual"),
|
|
3038
|
+
disabled: view.busyId !== void 0 || !onManualMerge,
|
|
3039
|
+
children: view.labels.manualMerge
|
|
2605
3040
|
}
|
|
2606
3041
|
)
|
|
2607
3042
|
] })
|
|
2608
3043
|
] }, conflict.id))
|
|
2609
3044
|
] }) : null,
|
|
2610
|
-
|
|
2611
|
-
/* @__PURE__ */ jsx16("h3", { children:
|
|
2612
|
-
/* @__PURE__ */ jsx16("p", { children: backupRecoveryDescription }),
|
|
3045
|
+
view.showBackupRecovery ? /* @__PURE__ */ jsxs10("section", { "data-recovery": "backup", children: [
|
|
3046
|
+
/* @__PURE__ */ jsx16("h3", { children: view.labels.backupRecovery }),
|
|
3047
|
+
/* @__PURE__ */ jsx16("p", { children: view.labels.backupRecoveryDescription }),
|
|
2613
3048
|
backup ?? (showBackupControls ? /* @__PURE__ */ jsx16(KeepBackup, {}) : null)
|
|
2614
3049
|
] }) : null,
|
|
2615
|
-
error ? /* @__PURE__ */ jsx16("p", { role: "alert", children: error instanceof Error ? error.message :
|
|
3050
|
+
view.error ? /* @__PURE__ */ jsx16("p", { id: "keepkit-sync-recovery-error", role: "alert", "aria-live": "assertive", children: view.error instanceof Error ? view.error.message : view.labels.error }) : null
|
|
2616
3051
|
]
|
|
2617
3052
|
}
|
|
2618
3053
|
);
|
|
2619
3054
|
}
|
|
3055
|
+
function ConflictPreview({
|
|
3056
|
+
item,
|
|
3057
|
+
heading,
|
|
3058
|
+
updatedAtLabel,
|
|
3059
|
+
noteLabel,
|
|
3060
|
+
side
|
|
3061
|
+
}) {
|
|
3062
|
+
return /* @__PURE__ */ jsxs10("article", { "data-conflict-version": side, "aria-label": heading, children: [
|
|
3063
|
+
/* @__PURE__ */ jsx16("h4", { children: heading }),
|
|
3064
|
+
/* @__PURE__ */ jsxs10("dl", { children: [
|
|
3065
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
3066
|
+
/* @__PURE__ */ jsx16("dt", { children: updatedAtLabel }),
|
|
3067
|
+
/* @__PURE__ */ jsx16("dd", { children: item ? /* @__PURE__ */ jsx16("time", { dateTime: new Date(item.updatedAt).toISOString(), children: formatConflictDate(item.updatedAt) }) : "\u2014" })
|
|
3068
|
+
] }),
|
|
3069
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
3070
|
+
/* @__PURE__ */ jsx16("dt", { children: noteLabel }),
|
|
3071
|
+
/* @__PURE__ */ jsx16("dd", { children: item?.note || "\u2014" })
|
|
3072
|
+
] })
|
|
3073
|
+
] })
|
|
3074
|
+
] });
|
|
3075
|
+
}
|
|
3076
|
+
function formatConflictDate(timestamp) {
|
|
3077
|
+
return new Date(timestamp).toISOString().slice(0, 10);
|
|
3078
|
+
}
|
|
2620
3079
|
|
|
2621
|
-
// src/
|
|
3080
|
+
// src/hooks/useKeepSyncStatusBanner.ts
|
|
2622
3081
|
import { useKeepContext as useKeepContext4 } from "@keepkit/core/react";
|
|
2623
|
-
|
|
2624
|
-
function KeepSyncStatusBanner({
|
|
2625
|
-
onRetry,
|
|
2626
|
-
onResolveConflicts,
|
|
2627
|
-
children,
|
|
2628
|
-
className,
|
|
2629
|
-
...props
|
|
2630
|
-
}) {
|
|
3082
|
+
function useKeepSyncStatusBanner({ onRetry, children }) {
|
|
2631
3083
|
const context = useKeepContext4();
|
|
2632
3084
|
const retryLabel = useUiLabel("retrySync");
|
|
2633
3085
|
const resolveLabel = useUiLabel("resolveSync");
|
|
@@ -2636,53 +3088,80 @@ function KeepSyncStatusBanner({
|
|
|
2636
3088
|
const syncedLabel = useUiLabel("syncSynced");
|
|
2637
3089
|
const status = context.syncState.status;
|
|
2638
3090
|
const hasConflicts = (context.syncState.conflicts?.length ?? 0) > 0 || context.syncState.conflictIds.length > 0;
|
|
2639
|
-
if (status === "idle" && !hasConflicts) return null;
|
|
2640
3091
|
const message = children ?? (status === "error" ? getErrorMessage4(context.syncState.error) : status === "conflict" || hasConflicts ? conflictLabel : status === "pending" || status === "syncing" ? pendingLabel : syncedLabel);
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
3092
|
+
return {
|
|
3093
|
+
status,
|
|
3094
|
+
hasConflicts,
|
|
3095
|
+
message,
|
|
3096
|
+
isMutating: context.isMutating,
|
|
3097
|
+
role: status === "error" || status === "conflict" || hasConflicts ? "alert" : "status",
|
|
3098
|
+
showRetry: status === "error" || status === "pending" || status === "syncing",
|
|
3099
|
+
retryLabel,
|
|
3100
|
+
resolveLabel,
|
|
3101
|
+
retry: async () => {
|
|
3102
|
+
if (onRetry) {
|
|
3103
|
+
await onRetry();
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
await context.flushSync();
|
|
2646
3107
|
}
|
|
2647
|
-
await context.flushSync();
|
|
2648
3108
|
};
|
|
3109
|
+
}
|
|
3110
|
+
function getErrorMessage4(error) {
|
|
3111
|
+
return error instanceof Error ? error.message : "Sync failed.";
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
// src/KeepSyncStatusBanner.tsx
|
|
3115
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3116
|
+
function KeepSyncStatusBanner({
|
|
3117
|
+
onRetry,
|
|
3118
|
+
onResolveConflicts,
|
|
3119
|
+
children,
|
|
3120
|
+
className,
|
|
3121
|
+
...props
|
|
3122
|
+
}) {
|
|
3123
|
+
const view = useKeepSyncStatusBanner({ onRetry, children });
|
|
3124
|
+
if (view.status === "idle" && !view.hasConflicts) return null;
|
|
2649
3125
|
return /* @__PURE__ */ jsxs11(
|
|
2650
3126
|
"aside",
|
|
2651
3127
|
{
|
|
2652
3128
|
...props,
|
|
2653
3129
|
className,
|
|
2654
|
-
role: props.role ?? role,
|
|
3130
|
+
role: props.role ?? view.role,
|
|
2655
3131
|
"aria-live": props["aria-live"] ?? "polite",
|
|
2656
3132
|
"data-keepkit": "sync-status",
|
|
2657
|
-
"data-state": status,
|
|
3133
|
+
"data-state": view.status,
|
|
2658
3134
|
children: [
|
|
2659
|
-
/* @__PURE__ */ jsx17("p", { children: message }),
|
|
2660
|
-
|
|
2661
|
-
|
|
3135
|
+
/* @__PURE__ */ jsx17("p", { children: view.message }),
|
|
3136
|
+
view.showRetry ? /* @__PURE__ */ jsx17(
|
|
3137
|
+
"button",
|
|
3138
|
+
{
|
|
3139
|
+
type: "button",
|
|
3140
|
+
"data-keep-action": "retry-sync",
|
|
3141
|
+
onClick: () => void view.retry(),
|
|
3142
|
+
disabled: view.isMutating,
|
|
3143
|
+
children: view.retryLabel
|
|
3144
|
+
}
|
|
3145
|
+
) : null,
|
|
3146
|
+
view.hasConflicts ? /* @__PURE__ */ jsx17(
|
|
3147
|
+
"button",
|
|
3148
|
+
{
|
|
3149
|
+
type: "button",
|
|
3150
|
+
"data-keep-action": "resolve-conflicts",
|
|
3151
|
+
onClick: onResolveConflicts,
|
|
3152
|
+
disabled: !onResolveConflicts,
|
|
3153
|
+
children: view.resolveLabel
|
|
3154
|
+
}
|
|
3155
|
+
) : null
|
|
2662
3156
|
]
|
|
2663
3157
|
}
|
|
2664
3158
|
);
|
|
2665
3159
|
}
|
|
2666
|
-
function getErrorMessage4(error) {
|
|
2667
|
-
return error instanceof Error ? error.message : "Sync failed.";
|
|
2668
|
-
}
|
|
2669
3160
|
|
|
2670
|
-
// src/
|
|
3161
|
+
// src/hooks/useKeepTagEditor.ts
|
|
2671
3162
|
import { useKeepItem as useKeepItem4 } from "@keepkit/core/react";
|
|
2672
3163
|
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState9 } from "react";
|
|
2673
|
-
|
|
2674
|
-
function KeepTagEditor({
|
|
2675
|
-
item,
|
|
2676
|
-
availableTags = [],
|
|
2677
|
-
onSaved,
|
|
2678
|
-
onSaveError,
|
|
2679
|
-
render,
|
|
2680
|
-
...props
|
|
2681
|
-
}) {
|
|
2682
|
-
const tagsLabel = useUiLabel("tagsToApply");
|
|
2683
|
-
const removeLabel = useUiLabel("remove");
|
|
2684
|
-
const applyTagsLabel = useUiLabel("applyTags");
|
|
2685
|
-
const errorLabel = useUiLabel("error");
|
|
3164
|
+
function useKeepTagEditor({ item, onSaved, onSaveError }) {
|
|
2686
3165
|
const itemState = useKeepItem4(item);
|
|
2687
3166
|
const [tags, setTags] = useState9(item.tags ?? []);
|
|
2688
3167
|
const [input, setInput] = useState9("");
|
|
@@ -2693,60 +3172,91 @@ function KeepTagEditor({
|
|
|
2693
3172
|
await itemState.updateTags(nextTags);
|
|
2694
3173
|
setTags(nextTags);
|
|
2695
3174
|
onSaved?.(nextTags);
|
|
2696
|
-
} catch (
|
|
2697
|
-
onSaveError?.(
|
|
2698
|
-
throw
|
|
3175
|
+
} catch (cause) {
|
|
3176
|
+
onSaveError?.(cause);
|
|
3177
|
+
throw cause;
|
|
2699
3178
|
}
|
|
2700
3179
|
}, [itemState, onSaveError, onSaved, tags]);
|
|
2701
3180
|
const addTag = (tag) => {
|
|
2702
3181
|
setTags(normalizeUiTags([...tags, tag]));
|
|
2703
3182
|
setInput("");
|
|
2704
3183
|
};
|
|
2705
|
-
const
|
|
3184
|
+
const state = { tags, setTags, save, isSaving: itemState.isMutating };
|
|
3185
|
+
return {
|
|
3186
|
+
state,
|
|
3187
|
+
input,
|
|
3188
|
+
setInput,
|
|
3189
|
+
error: itemState.error,
|
|
3190
|
+
handleInputKeyDown: (event) => {
|
|
3191
|
+
if (event.key === "Enter") {
|
|
3192
|
+
if (event.nativeEvent.isComposing) return;
|
|
3193
|
+
event.preventDefault();
|
|
3194
|
+
if (input.trim()) addTag(input);
|
|
3195
|
+
} else if (event.key === "Backspace" && input.length === 0 && tags.length > 0) {
|
|
3196
|
+
event.preventDefault();
|
|
3197
|
+
setTags(tags.slice(0, -1));
|
|
3198
|
+
}
|
|
3199
|
+
},
|
|
3200
|
+
removeTag: (tag) => setTags(tags.filter((current) => current !== tag)),
|
|
3201
|
+
submit: (event) => {
|
|
3202
|
+
event.preventDefault();
|
|
3203
|
+
void save().catch(() => void 0);
|
|
3204
|
+
},
|
|
3205
|
+
labels: {
|
|
3206
|
+
tags: useUiLabel("tagsToApply"),
|
|
3207
|
+
remove: useUiLabel("remove"),
|
|
3208
|
+
apply: useUiLabel("applyTags"),
|
|
3209
|
+
error: useUiLabel("error")
|
|
3210
|
+
}
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
// src/KeepTagEditor.tsx
|
|
3215
|
+
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3216
|
+
function KeepTagEditor({
|
|
3217
|
+
item,
|
|
3218
|
+
availableTags = [],
|
|
3219
|
+
onSaved,
|
|
3220
|
+
onSaveError,
|
|
3221
|
+
render,
|
|
3222
|
+
...props
|
|
3223
|
+
}) {
|
|
3224
|
+
const view = useKeepTagEditor({ item, onSaved, onSaveError });
|
|
3225
|
+
const { isSaving, tags } = view.state;
|
|
3226
|
+
const body = render ? render(view.state) : /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
2706
3227
|
/* @__PURE__ */ jsxs12("label", { children: [
|
|
2707
|
-
|
|
3228
|
+
view.labels.tags,
|
|
2708
3229
|
/* @__PURE__ */ jsx18(
|
|
2709
3230
|
"input",
|
|
2710
3231
|
{
|
|
2711
|
-
|
|
3232
|
+
"data-keep-action": "edit-tags",
|
|
3233
|
+
value: view.input,
|
|
2712
3234
|
list: availableTags.length > 0 ? `keep-tags-${item.id}` : void 0,
|
|
2713
|
-
onChange: (event) => setInput(event.currentTarget.value),
|
|
2714
|
-
onKeyDown:
|
|
2715
|
-
if (event.key === "Enter") {
|
|
2716
|
-
if (event.nativeEvent.isComposing) return;
|
|
2717
|
-
event.preventDefault();
|
|
2718
|
-
if (input.trim()) addTag(input);
|
|
2719
|
-
} else if (event.key === "Backspace" && input.length === 0 && tags.length > 0) {
|
|
2720
|
-
event.preventDefault();
|
|
2721
|
-
setTags(tags.slice(0, -1));
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
3235
|
+
onChange: (event) => view.setInput(event.currentTarget.value),
|
|
3236
|
+
onKeyDown: view.handleInputKeyDown
|
|
2724
3237
|
}
|
|
2725
3238
|
)
|
|
2726
3239
|
] }),
|
|
2727
3240
|
availableTags.length > 0 ? /* @__PURE__ */ jsx18("datalist", { id: `keep-tags-${item.id}`, children: availableTags.map((tag) => /* @__PURE__ */ jsx18("option", { value: tag }, tag)) }) : null,
|
|
2728
|
-
/* @__PURE__ */ jsx18("ul", { "aria-label":
|
|
3241
|
+
/* @__PURE__ */ jsx18("ul", { "aria-label": view.labels.tags, children: tags.map((tag) => /* @__PURE__ */ jsxs12("li", { children: [
|
|
2729
3242
|
tag,
|
|
2730
|
-
/* @__PURE__ */ jsx18("button", { type: "button", onClick: () =>
|
|
3243
|
+
/* @__PURE__ */ jsx18("button", { type: "button", "data-keep-action": "remove-tag", onClick: () => view.removeTag(tag), children: view.labels.remove })
|
|
2731
3244
|
] }, tag)) }),
|
|
2732
|
-
/* @__PURE__ */ jsx18("button", { type: "submit", disabled:
|
|
3245
|
+
/* @__PURE__ */ jsx18("button", { type: "submit", "data-keep-action": "apply-tags", disabled: isSaving, "aria-busy": isSaving, children: view.labels.apply })
|
|
2733
3246
|
] });
|
|
2734
3247
|
return /* @__PURE__ */ jsxs12(
|
|
2735
3248
|
"form",
|
|
2736
3249
|
{
|
|
2737
3250
|
...props,
|
|
2738
|
-
onSubmit:
|
|
2739
|
-
|
|
2740
|
-
void save().catch(() => void 0);
|
|
2741
|
-
},
|
|
2742
|
-
"aria-busy": itemState.isMutating || props["aria-busy"],
|
|
3251
|
+
onSubmit: view.submit,
|
|
3252
|
+
"aria-busy": isSaving || props["aria-busy"],
|
|
2743
3253
|
"data-keepkit": "tag-editor",
|
|
2744
|
-
"data-state":
|
|
2745
|
-
"data-loading":
|
|
2746
|
-
"data-disabled":
|
|
3254
|
+
"data-state": view.error ? "error" : isSaving ? "saving" : "idle",
|
|
3255
|
+
"data-loading": isSaving ? "true" : void 0,
|
|
3256
|
+
"data-disabled": isSaving ? "true" : void 0,
|
|
2747
3257
|
children: [
|
|
2748
3258
|
body,
|
|
2749
|
-
|
|
3259
|
+
view.error ? /* @__PURE__ */ jsx18("p", { role: "alert", children: getErrorMessage5(view.error, view.labels.error) }) : null
|
|
2750
3260
|
]
|
|
2751
3261
|
}
|
|
2752
3262
|
);
|
|
@@ -2755,23 +3265,84 @@ function getErrorMessage5(error, fallback) {
|
|
|
2755
3265
|
return error instanceof Error ? error.message : fallback;
|
|
2756
3266
|
}
|
|
2757
3267
|
|
|
2758
|
-
// src/
|
|
3268
|
+
// src/hooks/useKeepUndo.ts
|
|
2759
3269
|
import { useKeepContext as useKeepContext5 } from "@keepkit/core/react";
|
|
3270
|
+
function useKeepUndo() {
|
|
3271
|
+
const context = useKeepContext5();
|
|
3272
|
+
return {
|
|
3273
|
+
canUndo: context.undo.canUndo,
|
|
3274
|
+
undo: () => context.undoLastRemoval(),
|
|
3275
|
+
message: useUiLabel("undoAvailable"),
|
|
3276
|
+
label: useUiLabel("undo")
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
// src/KeepUndo.tsx
|
|
2760
3281
|
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2761
3282
|
function KeepUndo({ children, label, ...props }) {
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2764
|
-
const undoLabel = useUiLabel("undo");
|
|
2765
|
-
if (!context.undo.canUndo) return null;
|
|
3283
|
+
const view = useKeepUndo();
|
|
3284
|
+
if (!view.canUndo) return null;
|
|
2766
3285
|
return /* @__PURE__ */ jsxs13("div", { ...props, role: "status", "aria-live": "polite", "data-keepkit": "undo", "data-state": "available", children: [
|
|
2767
|
-
children ?? message,
|
|
2768
|
-
/* @__PURE__ */ jsx19("button", { type: "button", onClick: () => void
|
|
3286
|
+
children ?? view.message,
|
|
3287
|
+
/* @__PURE__ */ jsx19("button", { type: "button", "data-keep-action": "undo", onClick: () => void view.undo(), children: label ?? view.label })
|
|
2769
3288
|
] });
|
|
2770
3289
|
}
|
|
2771
3290
|
|
|
2772
3291
|
// src/status.tsx
|
|
3292
|
+
import { isValidElement as isValidElement6 } from "react";
|
|
3293
|
+
|
|
3294
|
+
// src/hooks/useStatusViews.ts
|
|
2773
3295
|
import { useKeepContext as useKeepContext6 } from "@keepkit/core/react";
|
|
2774
|
-
import {
|
|
3296
|
+
import { useEffect as useEffect6, useRef as useRef4, useState as useState10 } from "react";
|
|
3297
|
+
function useKeepEmptyState() {
|
|
3298
|
+
return useUiLabel("noItems").replace(/\.$/, "");
|
|
3299
|
+
}
|
|
3300
|
+
function useKeepStatus(status) {
|
|
3301
|
+
const context = useKeepContext6();
|
|
3302
|
+
const resolvedStatus = status ?? getDerivedStatus(context);
|
|
3303
|
+
const state = {
|
|
3304
|
+
status: resolvedStatus,
|
|
3305
|
+
error: context.error,
|
|
3306
|
+
pendingCount: context.syncState.pendingCount,
|
|
3307
|
+
items: context.items
|
|
3308
|
+
};
|
|
3309
|
+
return { state, defaultLabel: useUiLabel(getStatusLabelKey3(resolvedStatus)) };
|
|
3310
|
+
}
|
|
3311
|
+
function useKeepAnnouncements(messages) {
|
|
3312
|
+
const context = useKeepContext6();
|
|
3313
|
+
const savedMessage = useUiLabel("savedMessage", messages?.save);
|
|
3314
|
+
const removedMessage = useUiLabel("removedMessage", messages?.remove);
|
|
3315
|
+
const noteSavedMessage = useUiLabel("noteSavedMessage", messages?.note);
|
|
3316
|
+
const [message, setMessage] = useState10("");
|
|
3317
|
+
const lastChangeRef = useRef4(void 0);
|
|
3318
|
+
useEffect6(() => {
|
|
3319
|
+
const change = context.lastChange;
|
|
3320
|
+
if (!change || change === lastChangeRef.current) return;
|
|
3321
|
+
lastChangeRef.current = change;
|
|
3322
|
+
if (change.action === "save") setMessage(savedMessage);
|
|
3323
|
+
else if (change.action === "remove" || change.action === "removeBatch") setMessage(removedMessage);
|
|
3324
|
+
else if (change.action === "updateNote") setMessage(noteSavedMessage);
|
|
3325
|
+
}, [context.lastChange, noteSavedMessage, removedMessage, savedMessage]);
|
|
3326
|
+
return message;
|
|
3327
|
+
}
|
|
3328
|
+
function getDerivedStatus(context) {
|
|
3329
|
+
if (context.error) return "error";
|
|
3330
|
+
if (context.syncState.status === "pending" || context.syncState.status === "syncing") return "syncing";
|
|
3331
|
+
if (context.isMutating) return "saving";
|
|
3332
|
+
if (context.isLoading && !context.isHydrated) return "loading";
|
|
3333
|
+
if (context.isHydrated && context.items.length === 0) return "empty";
|
|
3334
|
+
return "idle";
|
|
3335
|
+
}
|
|
3336
|
+
function getStatusLabelKey3(status) {
|
|
3337
|
+
if (status === "empty") return "noItems";
|
|
3338
|
+
if (status === "loading") return "loadingItems";
|
|
3339
|
+
if (status === "error") return "error";
|
|
3340
|
+
if (status === "saving") return "saving";
|
|
3341
|
+
if (status === "syncing") return "syncing";
|
|
3342
|
+
return "saved";
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3345
|
+
// src/status.tsx
|
|
2775
3346
|
import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2776
3347
|
function KeepEmptyState({
|
|
2777
3348
|
title,
|
|
@@ -2782,7 +3353,7 @@ function KeepEmptyState({
|
|
|
2782
3353
|
className,
|
|
2783
3354
|
...rootProps
|
|
2784
3355
|
}) {
|
|
2785
|
-
const defaultTitle =
|
|
3356
|
+
const defaultTitle = useKeepEmptyState();
|
|
2786
3357
|
const contentChildren = asChild && isValidElement6(children) ? void 0 : children;
|
|
2787
3358
|
const body = contentChildren ?? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
2788
3359
|
/* @__PURE__ */ jsx20("h2", { children: title ?? defaultTitle }),
|
|
@@ -2806,18 +3377,10 @@ function KeepStatus({
|
|
|
2806
3377
|
className,
|
|
2807
3378
|
...rootProps
|
|
2808
3379
|
}) {
|
|
2809
|
-
const
|
|
3380
|
+
const view = useKeepStatus(status);
|
|
2810
3381
|
const contentChildren = asChild && isValidElement6(children) ? void 0 : children;
|
|
2811
|
-
const
|
|
2812
|
-
const
|
|
2813
|
-
const state = {
|
|
2814
|
-
status: resolvedStatus,
|
|
2815
|
-
error: context.error,
|
|
2816
|
-
pendingCount: context.syncState.pendingCount,
|
|
2817
|
-
items: context.items
|
|
2818
|
-
};
|
|
2819
|
-
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? labels?.[resolvedStatus] ?? defaultLabel;
|
|
2820
|
-
const role = rootProps.role ?? (resolvedStatus === "error" ? "alert" : "status");
|
|
3382
|
+
const body = render ? render(view.state) : typeof contentChildren === "function" ? contentChildren(view.state) : contentChildren ?? labels?.[view.state.status] ?? view.defaultLabel;
|
|
3383
|
+
const role = rootProps.role ?? (view.state.status === "error" ? "alert" : "status");
|
|
2821
3384
|
return renderRoot(
|
|
2822
3385
|
asChild,
|
|
2823
3386
|
isValidElement6(children) ? children : void 0,
|
|
@@ -2827,28 +3390,15 @@ function KeepStatus({
|
|
|
2827
3390
|
"data-keepkit": "status",
|
|
2828
3391
|
role,
|
|
2829
3392
|
"aria-live": rootProps["aria-live"] ?? "polite",
|
|
2830
|
-
"data-state":
|
|
2831
|
-
"data-loading":
|
|
3393
|
+
"data-state": view.state.status,
|
|
3394
|
+
"data-loading": view.state.status === "loading" || view.state.status === "saving" || view.state.status === "syncing" ? "true" : void 0
|
|
2832
3395
|
},
|
|
2833
3396
|
body,
|
|
2834
3397
|
"KeepStatus"
|
|
2835
3398
|
);
|
|
2836
3399
|
}
|
|
2837
3400
|
function KeepAnnouncements({ messages, ...props }) {
|
|
2838
|
-
const
|
|
2839
|
-
const savedMessage = useUiLabel("savedMessage", messages?.save);
|
|
2840
|
-
const removedMessage = useUiLabel("removedMessage", messages?.remove);
|
|
2841
|
-
const noteSavedMessage = useUiLabel("noteSavedMessage", messages?.note);
|
|
2842
|
-
const [message, setMessage] = useState10("");
|
|
2843
|
-
const lastChangeRef = useRef4(void 0);
|
|
2844
|
-
useEffect6(() => {
|
|
2845
|
-
const change = context.lastChange;
|
|
2846
|
-
if (!change || change === lastChangeRef.current) return;
|
|
2847
|
-
lastChangeRef.current = change;
|
|
2848
|
-
if (change.action === "save") setMessage(savedMessage);
|
|
2849
|
-
else if (change.action === "remove" || change.action === "removeBatch") setMessage(removedMessage);
|
|
2850
|
-
else if (change.action === "updateNote") setMessage(noteSavedMessage);
|
|
2851
|
-
}, [context.lastChange, noteSavedMessage, removedMessage, savedMessage]);
|
|
3401
|
+
const message = useKeepAnnouncements(messages);
|
|
2852
3402
|
return /* @__PURE__ */ jsx20(
|
|
2853
3403
|
"div",
|
|
2854
3404
|
{
|
|
@@ -2863,26 +3413,22 @@ function KeepAnnouncements({ messages, ...props }) {
|
|
|
2863
3413
|
);
|
|
2864
3414
|
}
|
|
2865
3415
|
var KeepAnnouncer = KeepAnnouncements;
|
|
2866
|
-
function getDerivedStatus(context) {
|
|
2867
|
-
if (context.error) return "error";
|
|
2868
|
-
if (context.syncState.status === "pending" || context.syncState.status === "syncing") return "syncing";
|
|
2869
|
-
if (context.isMutating) return "saving";
|
|
2870
|
-
if (context.isLoading && !context.isHydrated) return "loading";
|
|
2871
|
-
if (context.isHydrated && context.items.length === 0) return "empty";
|
|
2872
|
-
return "idle";
|
|
2873
|
-
}
|
|
2874
|
-
function getStatusLabelKey3(status) {
|
|
2875
|
-
if (status === "empty") return "noItems";
|
|
2876
|
-
if (status === "loading") return "loadingItems";
|
|
2877
|
-
if (status === "error") return "error";
|
|
2878
|
-
if (status === "saving") return "saving";
|
|
2879
|
-
if (status === "syncing") return "syncing";
|
|
2880
|
-
return "saved";
|
|
2881
|
-
}
|
|
2882
3416
|
|
|
2883
3417
|
// src/theme.tsx
|
|
2884
3418
|
import { cloneElement as cloneElement2, isValidElement as isValidElement7 } from "react";
|
|
2885
3419
|
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
3420
|
+
var keepThemeNames = [
|
|
3421
|
+
"default",
|
|
3422
|
+
"ocean",
|
|
3423
|
+
"forest",
|
|
3424
|
+
"sunset",
|
|
3425
|
+
"lavender",
|
|
3426
|
+
"compact",
|
|
3427
|
+
"minimal",
|
|
3428
|
+
"rounded",
|
|
3429
|
+
"high-contrast",
|
|
3430
|
+
"dark"
|
|
3431
|
+
];
|
|
2886
3432
|
function KeepThemeProvider({
|
|
2887
3433
|
children,
|
|
2888
3434
|
theme = "default",
|
|
@@ -3095,6 +3641,7 @@ export {
|
|
|
3095
3641
|
createStorageAdapter,
|
|
3096
3642
|
getKeepLocaleLabels,
|
|
3097
3643
|
isAllSelected,
|
|
3644
|
+
keepThemeNames,
|
|
3098
3645
|
toggleSelectAll,
|
|
3099
3646
|
useKeepContext7 as useKeepContext,
|
|
3100
3647
|
useKeepItem5 as useKeepItem,
|