@keepkit/ui 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.js +365 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nitta-a
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @keepkit/ui
|
|
2
|
+
|
|
3
|
+
[日本語](#日本語) | [English](#english)
|
|
4
|
+
|
|
5
|
+
## 日本語
|
|
6
|
+
|
|
7
|
+
`@keepkit/core` の保存状態を、アプリ固有のCSSやUIライブラリに接続するための、低依存・スタイルレスなReact UIパーツです。Tailwind CSS、shadcn/ui、その他のデザインシステムには依存しません。
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @keepkit/core @keepkit/ui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { KeepList, KeepProvider, KeepTagFilter } from "@keepkit/ui";
|
|
15
|
+
|
|
16
|
+
function SavedItems() {
|
|
17
|
+
return (
|
|
18
|
+
<KeepProvider>
|
|
19
|
+
<KeepTagFilter value={selectedTag} onValueChange={setSelectedTag} />
|
|
20
|
+
<KeepList options={{ tag: selectedTag }} />
|
|
21
|
+
</KeepProvider>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
提供するコンポーネントは次のとおりです。
|
|
27
|
+
|
|
28
|
+
- `KeepButton`: 保存済み状態、`aria-pressed`、ローディング・エラー状態、`asChild`、render props。`labels` または既存の `savedAriaLabel` / `unsavedAriaLabel` で翻訳できます。
|
|
29
|
+
- `KeepList`: 保存一覧、読み込み中・空・エラー状態、標準カード、削除操作。`renderItem` で行単位を置き換えられます。
|
|
30
|
+
- `KeepItemCard`: タイトル、画像、保存ボタン、削除ボタン。`getImageUrl` とrender propsで表示を差し替えられます。
|
|
31
|
+
- `KeepTagFilter`: タグと件数を表示するキーボード操作可能なフィルター。`value` / `onValueChange` のcontrolled APIに対応します。
|
|
32
|
+
- `KeepNoteEditor`: ノートの編集・保存フォーム。`render` で完全なカスタムUIにできます。
|
|
33
|
+
- `KeepEmptyState` / `KeepStatus`: 空、読み込み中、保存中、同期中、エラー状態の表示。
|
|
34
|
+
|
|
35
|
+
すべてのルート要素で `className` を利用でき、対応コンポーネントでは `asChild` で既存要素をルートとして利用できます。コンポーネントの標準HTMLは意味的な要素、ネイティブボタン、フォーム、`aria-live` を利用します。
|
|
36
|
+
|
|
37
|
+
## English
|
|
38
|
+
|
|
39
|
+
`@keepkit/ui` provides low-dependency, style-free React UI primitives that connect `@keepkit/core` state to an app's own CSS or design system. It has no direct dependency on Tailwind CSS, shadcn/ui, or another component library.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm add @keepkit/core @keepkit/ui
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { KeepList, KeepProvider, KeepTagFilter } from "@keepkit/ui";
|
|
47
|
+
|
|
48
|
+
function SavedItems() {
|
|
49
|
+
return (
|
|
50
|
+
<KeepProvider>
|
|
51
|
+
<KeepTagFilter value={selectedTag} onValueChange={setSelectedTag} />
|
|
52
|
+
<KeepList options={{ tag: selectedTag }} />
|
|
53
|
+
</KeepProvider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Components include `KeepButton`, `KeepList`, `KeepItemCard`, `KeepTagFilter`, `KeepNoteEditor`, `KeepEmptyState`, and `KeepStatus`. They support `className`, render props, localized labels, and `asChild` where a root element can be replaced. The defaults use semantic HTML, native keyboard behavior, `aria-pressed`, and live-region status announcements. `KeepList` includes loading, empty, error, and removal states, while `KeepTagFilter` exposes controlled `value` / `onValueChange` state.
|
|
59
|
+
|
|
60
|
+
The package re-exports `KeepProvider` and the core React hooks for convenient composition. The state and storage implementation remains in `@keepkit/core`; this package only adds the UI layer.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, HTMLAttributes, ReactElement, FormHTMLAttributes } from 'react';
|
|
3
|
+
import { KeepItem, KeepListOptions } from '@keepkit/core/core';
|
|
4
|
+
export { KeepItem, KeepItemInput, KeepListOptions, KeepSyncStatus } from '@keepkit/core/core';
|
|
5
|
+
import { KeepButtonProps as KeepButtonProps$1, UseKeepListResult } from '@keepkit/core/react';
|
|
6
|
+
export { KeepContextValue, KeepProvider, KeepProviderProps, useKeepContext, useKeepItem, useKeepList } from '@keepkit/core/react';
|
|
7
|
+
|
|
8
|
+
type RenderProp<TState> = (state: TState) => ReactNode;
|
|
9
|
+
type KeepButtonLabels = {
|
|
10
|
+
saved?: ReactNode;
|
|
11
|
+
unsaved?: ReactNode;
|
|
12
|
+
loading?: ReactNode;
|
|
13
|
+
error?: ReactNode;
|
|
14
|
+
savedAriaLabel?: string;
|
|
15
|
+
unsavedAriaLabel?: string;
|
|
16
|
+
};
|
|
17
|
+
type KeepButtonProps<TMeta = Record<string, unknown>> = KeepButtonProps$1<TMeta> & {
|
|
18
|
+
labels?: KeepButtonLabels;
|
|
19
|
+
};
|
|
20
|
+
/** A style-free save toggle with localized labels and the core button's ARIA/asChild behavior. */
|
|
21
|
+
declare function KeepButton<TMeta = Record<string, unknown>>({ labels, ...props }: KeepButtonProps<TMeta>): react.JSX.Element;
|
|
22
|
+
type KeepItemCardState<TMeta = Record<string, unknown>> = {
|
|
23
|
+
item: KeepItem<TMeta>;
|
|
24
|
+
isSaved: boolean;
|
|
25
|
+
isMutating: boolean;
|
|
26
|
+
error: unknown | null;
|
|
27
|
+
remove: () => Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
type KeepItemCardProps<TMeta = Record<string, unknown>> = Omit<HTMLAttributes<HTMLElement>, "children" | "title"> & {
|
|
30
|
+
item: KeepItem<TMeta>;
|
|
31
|
+
title?: ReactNode | ((item: KeepItem<TMeta>) => ReactNode);
|
|
32
|
+
getImageUrl?: (item: KeepItem<TMeta>) => string | undefined;
|
|
33
|
+
imageAlt?: string;
|
|
34
|
+
render?: RenderProp<KeepItemCardState<TMeta>>;
|
|
35
|
+
children?: ReactNode | RenderProp<KeepItemCardState<TMeta>>;
|
|
36
|
+
removeLabel?: string;
|
|
37
|
+
onRemoveError?: (error: unknown) => void;
|
|
38
|
+
onRemoved?: (item: KeepItem<TMeta>) => void;
|
|
39
|
+
showSaveButton?: boolean;
|
|
40
|
+
saveButtonLabels?: KeepButtonLabels;
|
|
41
|
+
asChild?: boolean;
|
|
42
|
+
};
|
|
43
|
+
/** A low-dependency item presentation primitive. The default markup can be replaced with render props. */
|
|
44
|
+
declare function KeepItemCard<TMeta = Record<string, unknown>>({ item, title, getImageUrl, imageAlt, render, children, removeLabel, onRemoveError, onRemoved, showSaveButton, saveButtonLabels, asChild, className, ...rootProps }: KeepItemCardProps<TMeta>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
45
|
+
type KeepListState<TMeta = Record<string, unknown>> = UseKeepListResult<TMeta>;
|
|
46
|
+
type KeepListProps<TMeta = Record<string, unknown>> = Omit<HTMLAttributes<HTMLElement>, "children"> & {
|
|
47
|
+
options?: KeepListOptions<TMeta>;
|
|
48
|
+
children?: ReactNode | RenderProp<KeepListState<TMeta>>;
|
|
49
|
+
renderItem?: (item: KeepItem<TMeta>, state: KeepListState<TMeta>) => ReactNode;
|
|
50
|
+
loading?: ReactNode | RenderProp<KeepListState<TMeta>>;
|
|
51
|
+
empty?: ReactNode | RenderProp<KeepListState<TMeta>>;
|
|
52
|
+
error?: ReactNode | RenderProp<KeepListState<TMeta>>;
|
|
53
|
+
itemCardProps?: Omit<KeepItemCardProps<TMeta>, "item" | "children" | "render">;
|
|
54
|
+
asChild?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** A list primitive with built-in loading, empty, error, and removal states. */
|
|
57
|
+
declare function KeepList<TMeta = Record<string, unknown>>({ options, children, renderItem, loading, empty, error: errorContent, itemCardProps, asChild, className, ...rootProps }: KeepListProps<TMeta>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
58
|
+
type KeepTagFilterState = {
|
|
59
|
+
tags: string[];
|
|
60
|
+
tagCounts: Record<string, number>;
|
|
61
|
+
value?: string;
|
|
62
|
+
select: (tag?: string) => void;
|
|
63
|
+
};
|
|
64
|
+
type KeepTagFilterProps<TMeta = Record<string, unknown>> = Omit<HTMLAttributes<HTMLElement>, "children" | "onChange"> & {
|
|
65
|
+
listOptions?: KeepListOptions<TMeta>;
|
|
66
|
+
value?: string;
|
|
67
|
+
defaultValue?: string;
|
|
68
|
+
onChange?: (tag?: string) => void;
|
|
69
|
+
onValueChange?: (tag?: string) => void;
|
|
70
|
+
allLabel?: ReactNode;
|
|
71
|
+
ariaLabel?: string;
|
|
72
|
+
renderTag?: (tag: string, count: number, selected: boolean) => ReactNode;
|
|
73
|
+
render?: RenderProp<KeepTagFilterState>;
|
|
74
|
+
children?: ReactNode | RenderProp<KeepTagFilterState>;
|
|
75
|
+
asChild?: boolean;
|
|
76
|
+
};
|
|
77
|
+
/** An ARIA button group for tag filtering; consumers can connect value to KeepList options. */
|
|
78
|
+
declare function KeepTagFilter<TMeta = Record<string, unknown>>({ listOptions, value: controlledValue, defaultValue, onChange, onValueChange, allLabel, ariaLabel, renderTag, render, children, asChild, className, ...rootProps }: KeepTagFilterProps<TMeta>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
79
|
+
type KeepNoteEditorState<TMeta = Record<string, unknown>> = {
|
|
80
|
+
item: KeepItem<TMeta>;
|
|
81
|
+
note: string;
|
|
82
|
+
setNote: (note: string) => void;
|
|
83
|
+
isDirty: boolean;
|
|
84
|
+
isSaving: boolean;
|
|
85
|
+
error: unknown | null;
|
|
86
|
+
save: () => Promise<void>;
|
|
87
|
+
};
|
|
88
|
+
type KeepNoteEditorProps<TMeta = Record<string, unknown>> = Omit<FormHTMLAttributes<HTMLFormElement>, "children" | "onSubmit"> & {
|
|
89
|
+
item: KeepItem<TMeta>;
|
|
90
|
+
label?: ReactNode;
|
|
91
|
+
saveLabel?: ReactNode;
|
|
92
|
+
placeholder?: string;
|
|
93
|
+
onSaved?: (note?: string) => void;
|
|
94
|
+
onSaveError?: (error: unknown) => void;
|
|
95
|
+
render?: RenderProp<KeepNoteEditorState<TMeta>>;
|
|
96
|
+
children?: ReactNode | RenderProp<KeepNoteEditorState<TMeta>>;
|
|
97
|
+
asChild?: boolean;
|
|
98
|
+
};
|
|
99
|
+
/** A controlled-by-default note editor that persists through useKeepItem. */
|
|
100
|
+
declare function KeepNoteEditor<TMeta = Record<string, unknown>>({ item, label, saveLabel, placeholder, onSaved, onSaveError, render, children, asChild, className, ...formProps }: KeepNoteEditorProps<TMeta>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
101
|
+
type KeepEmptyStateProps = Omit<HTMLAttributes<HTMLElement>, "children" | "title"> & {
|
|
102
|
+
title?: ReactNode;
|
|
103
|
+
description?: ReactNode;
|
|
104
|
+
action?: ReactNode;
|
|
105
|
+
children?: ReactNode;
|
|
106
|
+
asChild?: boolean;
|
|
107
|
+
};
|
|
108
|
+
/** A style-free, reusable empty collection state. */
|
|
109
|
+
declare function KeepEmptyState({ title, description, action, children, asChild, className, ...rootProps }: KeepEmptyStateProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
110
|
+
type KeepStatusValue = "idle" | "empty" | "loading" | "saving" | "syncing" | "error";
|
|
111
|
+
type KeepStatusLabels = Partial<Record<KeepStatusValue, ReactNode>>;
|
|
112
|
+
type KeepStatusState<TMeta = Record<string, unknown>> = {
|
|
113
|
+
status: KeepStatusValue;
|
|
114
|
+
error: unknown | null;
|
|
115
|
+
pendingCount: number;
|
|
116
|
+
items: KeepItem<TMeta>[];
|
|
117
|
+
};
|
|
118
|
+
type KeepStatusProps<TMeta = Record<string, unknown>> = Omit<HTMLAttributes<HTMLElement>, "children"> & {
|
|
119
|
+
status?: KeepStatusValue;
|
|
120
|
+
labels?: KeepStatusLabels;
|
|
121
|
+
children?: ReactNode | RenderProp<KeepStatusState<TMeta>>;
|
|
122
|
+
render?: RenderProp<KeepStatusState<TMeta>>;
|
|
123
|
+
asChild?: boolean;
|
|
124
|
+
};
|
|
125
|
+
/** Presents provider loading, mutation, synchronization, empty, and error states accessibly. */
|
|
126
|
+
declare function KeepStatus<TMeta = Record<string, unknown>>({ status, labels, children, render, asChild, className, ...rootProps }: KeepStatusProps<TMeta>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
127
|
+
|
|
128
|
+
export { KeepButton, type KeepButtonLabels, type KeepButtonProps, KeepEmptyState, type KeepEmptyStateProps, KeepItemCard, type KeepItemCardProps, type KeepItemCardState, KeepList, type KeepListProps, type KeepListState, KeepNoteEditor, type KeepNoteEditorProps, type KeepNoteEditorState, KeepStatus, type KeepStatusLabels, type KeepStatusProps, type KeepStatusState, type KeepStatusValue, KeepTagFilter, type KeepTagFilterProps, type KeepTagFilterState, type RenderProp };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/index.tsx
|
|
4
|
+
import {
|
|
5
|
+
KeepButton as CoreKeepButton,
|
|
6
|
+
useKeepContext,
|
|
7
|
+
useKeepItem,
|
|
8
|
+
useKeepList
|
|
9
|
+
} from "@keepkit/core/react";
|
|
10
|
+
import {
|
|
11
|
+
cloneElement,
|
|
12
|
+
isValidElement,
|
|
13
|
+
useCallback,
|
|
14
|
+
useEffect,
|
|
15
|
+
useMemo,
|
|
16
|
+
useState
|
|
17
|
+
} from "react";
|
|
18
|
+
import { KeepProvider, useKeepContext as useKeepContext2, useKeepItem as useKeepItem2, useKeepList as useKeepList2 } from "@keepkit/core/react";
|
|
19
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
function KeepButton({ labels, ...props }) {
|
|
21
|
+
const buttonState = useKeepItem(props.item.id, {
|
|
22
|
+
meta: props.item.meta,
|
|
23
|
+
targetType: props.item.targetType,
|
|
24
|
+
note: props.item.note,
|
|
25
|
+
tags: props.item.tags
|
|
26
|
+
});
|
|
27
|
+
const customStateLabel = labels?.loading !== void 0 || labels?.error !== void 0;
|
|
28
|
+
const getStateContent = (state) => {
|
|
29
|
+
if (state.error && labels?.error !== void 0) return labels.error;
|
|
30
|
+
if (state.isMutating && labels?.loading !== void 0) return labels.loading;
|
|
31
|
+
if (typeof props.children === "function") return props.children(state);
|
|
32
|
+
if (props.children !== void 0) return props.children;
|
|
33
|
+
return state.isSaved ? labels?.saved ?? "Saved" : labels?.unsaved ?? "Save";
|
|
34
|
+
};
|
|
35
|
+
if (props.asChild === true) {
|
|
36
|
+
const sharedProps2 = {
|
|
37
|
+
...props,
|
|
38
|
+
"aria-busy": props["aria-busy"] ?? (buttonState.isLoading || buttonState.isMutating),
|
|
39
|
+
savedLabel: labels?.saved ?? props.savedLabel,
|
|
40
|
+
unsavedLabel: labels?.unsaved ?? props.unsavedLabel,
|
|
41
|
+
savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,
|
|
42
|
+
unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel
|
|
43
|
+
};
|
|
44
|
+
if (!customStateLabel) return /* @__PURE__ */ jsx(CoreKeepButton, { ...sharedProps2 });
|
|
45
|
+
const statefulProps2 = {
|
|
46
|
+
...sharedProps2,
|
|
47
|
+
children: (state) => /* @__PURE__ */ jsx(Fragment, { children: getStateContent(state) })
|
|
48
|
+
};
|
|
49
|
+
return /* @__PURE__ */ jsx(CoreKeepButton, { ...statefulProps2 });
|
|
50
|
+
}
|
|
51
|
+
const sharedProps = {
|
|
52
|
+
...props,
|
|
53
|
+
"aria-busy": props["aria-busy"] ?? (buttonState.isLoading || buttonState.isMutating),
|
|
54
|
+
savedLabel: labels?.saved ?? props.savedLabel,
|
|
55
|
+
unsavedLabel: labels?.unsaved ?? props.unsavedLabel,
|
|
56
|
+
savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,
|
|
57
|
+
unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel
|
|
58
|
+
};
|
|
59
|
+
if (!customStateLabel) return /* @__PURE__ */ jsx(CoreKeepButton, { ...sharedProps });
|
|
60
|
+
const statefulProps = { ...sharedProps, children: (state) => getStateContent(state) };
|
|
61
|
+
return /* @__PURE__ */ jsx(CoreKeepButton, { ...statefulProps });
|
|
62
|
+
}
|
|
63
|
+
function KeepItemCard({
|
|
64
|
+
item,
|
|
65
|
+
title,
|
|
66
|
+
getImageUrl,
|
|
67
|
+
imageAlt,
|
|
68
|
+
render,
|
|
69
|
+
children,
|
|
70
|
+
removeLabel = "Remove",
|
|
71
|
+
onRemoveError,
|
|
72
|
+
onRemoved,
|
|
73
|
+
showSaveButton = true,
|
|
74
|
+
saveButtonLabels,
|
|
75
|
+
asChild = false,
|
|
76
|
+
className,
|
|
77
|
+
...rootProps
|
|
78
|
+
}) {
|
|
79
|
+
const itemState = useKeepItem(item.id);
|
|
80
|
+
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
81
|
+
const state = {
|
|
82
|
+
item,
|
|
83
|
+
isSaved: itemState.isSaved,
|
|
84
|
+
isMutating: itemState.isMutating,
|
|
85
|
+
error: itemState.error,
|
|
86
|
+
remove: itemState.remove
|
|
87
|
+
};
|
|
88
|
+
const resolvedTitle = typeof title === "function" ? title(item) : title ?? getMetaTitle(item.meta) ?? item.id;
|
|
89
|
+
const imageUrl = getImageUrl?.(item);
|
|
90
|
+
async function handleRemove() {
|
|
91
|
+
try {
|
|
92
|
+
await itemState.remove();
|
|
93
|
+
onRemoved?.(item);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
onRemoveError?.(error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
99
|
+
imageUrl ? /* @__PURE__ */ jsx("img", { src: imageUrl, alt: imageAlt ?? String(resolvedTitle) }) : null,
|
|
100
|
+
/* @__PURE__ */ jsx("h3", { children: resolvedTitle }),
|
|
101
|
+
showSaveButton ? /* @__PURE__ */ jsx(
|
|
102
|
+
KeepButton,
|
|
103
|
+
{
|
|
104
|
+
item: toKeepButtonItem(item),
|
|
105
|
+
labels: saveButtonLabels,
|
|
106
|
+
getAriaLabel: (buttonState) => `${buttonState.isSaved ? "Remove" : "Save"} ${String(resolvedTitle)}`
|
|
107
|
+
}
|
|
108
|
+
) : null,
|
|
109
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void handleRemove(), disabled: itemState.isMutating, children: removeLabel })
|
|
110
|
+
] });
|
|
111
|
+
return renderRoot(
|
|
112
|
+
asChild,
|
|
113
|
+
isValidElement(children) ? children : void 0,
|
|
114
|
+
{ ...rootProps, className, "aria-busy": itemState.isMutating || rootProps["aria-busy"] },
|
|
115
|
+
body,
|
|
116
|
+
"KeepItemCard"
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function KeepList({
|
|
120
|
+
options,
|
|
121
|
+
children,
|
|
122
|
+
renderItem,
|
|
123
|
+
loading = "Loading saved items\u2026",
|
|
124
|
+
empty = "No saved items.",
|
|
125
|
+
error: errorContent = "Could not load saved items.",
|
|
126
|
+
itemCardProps,
|
|
127
|
+
asChild = false,
|
|
128
|
+
className,
|
|
129
|
+
...rootProps
|
|
130
|
+
}) {
|
|
131
|
+
const state = useKeepList(options);
|
|
132
|
+
const body = getListBody(state, { children, renderItem, loading, empty, error: errorContent, itemCardProps });
|
|
133
|
+
const root = asChild && isValidElement(children) ? children : void 0;
|
|
134
|
+
return renderRoot(
|
|
135
|
+
asChild,
|
|
136
|
+
root,
|
|
137
|
+
{ ...rootProps, className, "aria-busy": state.isLoading || rootProps["aria-busy"] },
|
|
138
|
+
body,
|
|
139
|
+
"KeepList"
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
function getListBody(state, options) {
|
|
143
|
+
if (state.error && state.items.length === 0) return resolveContent(options.error, state);
|
|
144
|
+
if (state.isLoading && !state.isHydrated) return resolveContent(options.loading, state);
|
|
145
|
+
if (state.isHydrated && state.items.length === 0) return resolveContent(options.empty, state);
|
|
146
|
+
if (typeof options.children === "function") return options.children(state);
|
|
147
|
+
if (options.children !== void 0 && !isValidElement(options.children)) return options.children;
|
|
148
|
+
const items = state.items.map(
|
|
149
|
+
(item) => options.renderItem ? options.renderItem(item, state) : /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(KeepItemCard, { item, ...options.itemCardProps }) }, item.id)
|
|
150
|
+
);
|
|
151
|
+
return /* @__PURE__ */ jsx("ul", { children: items });
|
|
152
|
+
}
|
|
153
|
+
function KeepTagFilter({
|
|
154
|
+
listOptions,
|
|
155
|
+
value: controlledValue,
|
|
156
|
+
defaultValue,
|
|
157
|
+
onChange,
|
|
158
|
+
onValueChange,
|
|
159
|
+
allLabel = "All",
|
|
160
|
+
ariaLabel = "Filter saved items by tag",
|
|
161
|
+
renderTag,
|
|
162
|
+
render,
|
|
163
|
+
children,
|
|
164
|
+
asChild = false,
|
|
165
|
+
className,
|
|
166
|
+
...rootProps
|
|
167
|
+
}) {
|
|
168
|
+
const list = useKeepList(listOptions);
|
|
169
|
+
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
170
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
171
|
+
const value = controlledValue ?? uncontrolledValue;
|
|
172
|
+
const select = useCallback(
|
|
173
|
+
(tag) => {
|
|
174
|
+
if (controlledValue === void 0) setUncontrolledValue(tag);
|
|
175
|
+
onChange?.(tag);
|
|
176
|
+
onValueChange?.(tag);
|
|
177
|
+
},
|
|
178
|
+
[controlledValue, onChange, onValueChange]
|
|
179
|
+
);
|
|
180
|
+
const state = useMemo(
|
|
181
|
+
() => ({ tags: list.tags, tagCounts: list.tagCounts, value, select }),
|
|
182
|
+
[list.tagCounts, list.tags, select, value]
|
|
183
|
+
);
|
|
184
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs("fieldset", { children: [
|
|
185
|
+
/* @__PURE__ */ jsx("legend", { children: ariaLabel }),
|
|
186
|
+
/* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": value === void 0, onClick: () => select(), children: allLabel }),
|
|
187
|
+
list.tags.map((tag) => /* @__PURE__ */ jsxs("button", { type: "button", "aria-pressed": value === tag, onClick: () => select(tag), children: [
|
|
188
|
+
renderTag ? renderTag(tag, list.tagCounts[tag] ?? 0, value === tag) : tag,
|
|
189
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
190
|
+
" (",
|
|
191
|
+
list.tagCounts[tag] ?? 0,
|
|
192
|
+
")"
|
|
193
|
+
] })
|
|
194
|
+
] }, tag))
|
|
195
|
+
] });
|
|
196
|
+
return renderRoot(
|
|
197
|
+
asChild,
|
|
198
|
+
isValidElement(children) ? children : void 0,
|
|
199
|
+
{ ...rootProps, className },
|
|
200
|
+
body,
|
|
201
|
+
"KeepTagFilter"
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
function KeepNoteEditor({
|
|
205
|
+
item,
|
|
206
|
+
label = "Note",
|
|
207
|
+
saveLabel = "Save note",
|
|
208
|
+
placeholder,
|
|
209
|
+
onSaved,
|
|
210
|
+
onSaveError,
|
|
211
|
+
render,
|
|
212
|
+
children,
|
|
213
|
+
asChild = false,
|
|
214
|
+
className,
|
|
215
|
+
...formProps
|
|
216
|
+
}) {
|
|
217
|
+
const itemState = useKeepItem(item.id);
|
|
218
|
+
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
219
|
+
const [note, setNote] = useState(item.note ?? "");
|
|
220
|
+
useEffect(() => setNote(itemState.item?.note ?? item.note ?? ""), [item.note, itemState.item?.note]);
|
|
221
|
+
const save = useCallback(async () => {
|
|
222
|
+
const nextNote = note.trim() || void 0;
|
|
223
|
+
try {
|
|
224
|
+
await itemState.updateNote(nextNote);
|
|
225
|
+
onSaved?.(nextNote);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
onSaveError?.(error);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}, [itemState, note, onSaveError, onSaved]);
|
|
231
|
+
const state = {
|
|
232
|
+
item,
|
|
233
|
+
note,
|
|
234
|
+
setNote,
|
|
235
|
+
isDirty: note !== (itemState.item?.note ?? item.note ?? ""),
|
|
236
|
+
isSaving: itemState.isMutating,
|
|
237
|
+
error: itemState.error,
|
|
238
|
+
save
|
|
239
|
+
};
|
|
240
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
241
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
242
|
+
label,
|
|
243
|
+
/* @__PURE__ */ jsx(
|
|
244
|
+
"textarea",
|
|
245
|
+
{
|
|
246
|
+
value: note,
|
|
247
|
+
onChange: (event) => setNote(event.currentTarget.value),
|
|
248
|
+
placeholder,
|
|
249
|
+
disabled: itemState.isMutating
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] }),
|
|
253
|
+
/* @__PURE__ */ jsx("button", { type: "submit", disabled: itemState.isMutating, "aria-busy": itemState.isMutating, children: saveLabel })
|
|
254
|
+
] });
|
|
255
|
+
const handleSubmit = (event) => {
|
|
256
|
+
event.preventDefault();
|
|
257
|
+
void save().catch(() => void 0);
|
|
258
|
+
};
|
|
259
|
+
return renderRoot(
|
|
260
|
+
asChild,
|
|
261
|
+
isValidElement(children) ? children : void 0,
|
|
262
|
+
{ ...formProps, className, onSubmit: handleSubmit, "aria-busy": itemState.isMutating || formProps["aria-busy"] },
|
|
263
|
+
body,
|
|
264
|
+
"KeepNoteEditor"
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
function KeepEmptyState({
|
|
268
|
+
title = "No saved items",
|
|
269
|
+
description,
|
|
270
|
+
action,
|
|
271
|
+
children,
|
|
272
|
+
asChild = false,
|
|
273
|
+
className,
|
|
274
|
+
...rootProps
|
|
275
|
+
}) {
|
|
276
|
+
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
277
|
+
const body = contentChildren ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
278
|
+
/* @__PURE__ */ jsx("h2", { children: title }),
|
|
279
|
+
description ? /* @__PURE__ */ jsx("p", { children: description }) : null,
|
|
280
|
+
action
|
|
281
|
+
] });
|
|
282
|
+
return renderRoot(asChild, children, { ...rootProps, className }, body, "KeepEmptyState");
|
|
283
|
+
}
|
|
284
|
+
function KeepStatus({
|
|
285
|
+
status,
|
|
286
|
+
labels,
|
|
287
|
+
children,
|
|
288
|
+
render,
|
|
289
|
+
asChild = false,
|
|
290
|
+
className,
|
|
291
|
+
...rootProps
|
|
292
|
+
}) {
|
|
293
|
+
const context = useKeepContext();
|
|
294
|
+
const contentChildren = asChild && isValidElement(children) ? void 0 : children;
|
|
295
|
+
const resolvedStatus = status ?? getDerivedStatus(context);
|
|
296
|
+
const state = {
|
|
297
|
+
status: resolvedStatus,
|
|
298
|
+
error: context.error,
|
|
299
|
+
pendingCount: context.syncState.pendingCount,
|
|
300
|
+
items: context.items
|
|
301
|
+
};
|
|
302
|
+
const body = render ? render(state) : typeof contentChildren === "function" ? contentChildren(state) : contentChildren ?? labels?.[resolvedStatus] ?? getDefaultStatusLabel(resolvedStatus);
|
|
303
|
+
const role = rootProps.role ?? (resolvedStatus === "error" ? "alert" : "status");
|
|
304
|
+
return renderRoot(
|
|
305
|
+
asChild,
|
|
306
|
+
isValidElement(children) ? children : void 0,
|
|
307
|
+
{ ...rootProps, className, role, "aria-live": rootProps["aria-live"] ?? "polite" },
|
|
308
|
+
body,
|
|
309
|
+
"KeepStatus"
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
function getDerivedStatus(context) {
|
|
313
|
+
if (context.error) return "error";
|
|
314
|
+
if (context.syncState.status === "pending" || context.syncState.status === "syncing") return "syncing";
|
|
315
|
+
if (context.isMutating) return "saving";
|
|
316
|
+
if (context.isLoading && !context.isHydrated) return "loading";
|
|
317
|
+
if (context.isHydrated && context.items.length === 0) return "empty";
|
|
318
|
+
return "idle";
|
|
319
|
+
}
|
|
320
|
+
function getDefaultStatusLabel(status) {
|
|
321
|
+
if (status === "empty") return "No saved items.";
|
|
322
|
+
if (status === "loading") return "Loading saved items\u2026";
|
|
323
|
+
if (status === "saving") return "Saving\u2026";
|
|
324
|
+
if (status === "syncing") return "Syncing\u2026";
|
|
325
|
+
if (status === "error") return "Something went wrong.";
|
|
326
|
+
return "";
|
|
327
|
+
}
|
|
328
|
+
function resolveContent(content, state) {
|
|
329
|
+
return typeof content === "function" ? content(state) : content;
|
|
330
|
+
}
|
|
331
|
+
function toKeepButtonItem(item) {
|
|
332
|
+
return {
|
|
333
|
+
id: item.id,
|
|
334
|
+
meta: item.meta,
|
|
335
|
+
targetType: item.targetType,
|
|
336
|
+
note: item.note,
|
|
337
|
+
tags: item.tags
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function getMetaTitle(meta) {
|
|
341
|
+
if (typeof meta !== "object" || meta === null || !("title" in meta)) return void 0;
|
|
342
|
+
const title = meta.title;
|
|
343
|
+
return typeof title === "string" && title.trim() ? title.trim() : void 0;
|
|
344
|
+
}
|
|
345
|
+
function renderRoot(asChild, child, props, body, componentName) {
|
|
346
|
+
if (asChild) {
|
|
347
|
+
if (!isValidElement(child)) throw new Error(`${componentName} with asChild requires a single React element child.`);
|
|
348
|
+
return cloneElement(child, { ...props, children: body });
|
|
349
|
+
}
|
|
350
|
+
return /* @__PURE__ */ jsx("div", { ...props, children: body });
|
|
351
|
+
}
|
|
352
|
+
export {
|
|
353
|
+
KeepButton,
|
|
354
|
+
KeepEmptyState,
|
|
355
|
+
KeepItemCard,
|
|
356
|
+
KeepList,
|
|
357
|
+
KeepNoteEditor,
|
|
358
|
+
KeepProvider,
|
|
359
|
+
KeepStatus,
|
|
360
|
+
KeepTagFilter,
|
|
361
|
+
useKeepContext2 as useKeepContext,
|
|
362
|
+
useKeepItem2 as useKeepItem,
|
|
363
|
+
useKeepList2 as useKeepList
|
|
364
|
+
};
|
|
365
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["\"use client\";\n\nimport type { KeepItem, KeepItemInput, KeepListOptions, KeepSyncStatus } from \"@keepkit/core/core\";\nimport type { UseKeepListResult } from \"@keepkit/core/react\";\nimport {\n KeepButton as CoreKeepButton,\n type KeepButtonProps as CoreKeepButtonProps,\n type KeepButtonState,\n useKeepContext,\n useKeepItem,\n useKeepList,\n} from \"@keepkit/core/react\";\nimport {\n cloneElement,\n type FormHTMLAttributes,\n type HTMLAttributes,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nexport type { KeepContextValue, KeepProviderProps } from \"@keepkit/core/react\";\nexport { KeepProvider, useKeepContext, useKeepItem, useKeepList } from \"@keepkit/core/react\";\nexport type { KeepItem, KeepItemInput, KeepListOptions, KeepSyncStatus };\n\nexport type RenderProp<TState> = (state: TState) => ReactNode;\n\nexport type KeepButtonLabels = {\n saved?: ReactNode;\n unsaved?: ReactNode;\n loading?: ReactNode;\n error?: ReactNode;\n savedAriaLabel?: string;\n unsavedAriaLabel?: string;\n};\n\nexport type KeepButtonProps<TMeta = Record<string, unknown>> = CoreKeepButtonProps<TMeta> & {\n labels?: KeepButtonLabels;\n};\n\n/** A style-free save toggle with localized labels and the core button's ARIA/asChild behavior. */\nexport function KeepButton<TMeta = Record<string, unknown>>({ labels, ...props }: KeepButtonProps<TMeta>) {\n const buttonState = useKeepItem<TMeta>(props.item.id, {\n meta: props.item.meta,\n targetType: props.item.targetType,\n note: props.item.note,\n tags: props.item.tags,\n });\n const customStateLabel = labels?.loading !== undefined || labels?.error !== undefined;\n const getStateContent = (state: KeepButtonState<TMeta>): ReactNode => {\n if (state.error && labels?.error !== undefined) return labels.error;\n if (state.isMutating && labels?.loading !== undefined) return labels.loading;\n if (typeof props.children === \"function\") return props.children(state);\n if (props.children !== undefined) return props.children;\n return state.isSaved ? (labels?.saved ?? \"Saved\") : (labels?.unsaved ?? \"Save\");\n };\n if (props.asChild === true) {\n const sharedProps = {\n ...props,\n \"aria-busy\": props[\"aria-busy\"] ?? (buttonState.isLoading || buttonState.isMutating),\n savedLabel: labels?.saved ?? props.savedLabel,\n unsavedLabel: labels?.unsaved ?? props.unsavedLabel,\n savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,\n unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel,\n };\n if (!customStateLabel) return <CoreKeepButton<TMeta> {...sharedProps} />;\n const statefulProps = {\n ...sharedProps,\n children: (state: KeepButtonState<TMeta>) => <>{getStateContent(state)}</>,\n };\n return <CoreKeepButton<TMeta> {...statefulProps} />;\n }\n const sharedProps = {\n ...props,\n \"aria-busy\": props[\"aria-busy\"] ?? (buttonState.isLoading || buttonState.isMutating),\n savedLabel: labels?.saved ?? props.savedLabel,\n unsavedLabel: labels?.unsaved ?? props.unsavedLabel,\n savedAriaLabel: labels?.savedAriaLabel ?? props.savedAriaLabel,\n unsavedAriaLabel: labels?.unsavedAriaLabel ?? props.unsavedAriaLabel,\n };\n if (!customStateLabel) return <CoreKeepButton<TMeta> {...sharedProps} />;\n const statefulProps = { ...sharedProps, children: (state: KeepButtonState<TMeta>) => getStateContent(state) };\n return <CoreKeepButton<TMeta> {...statefulProps} />;\n}\n\nexport type KeepItemCardState<TMeta = Record<string, unknown>> = {\n item: KeepItem<TMeta>;\n isSaved: boolean;\n isMutating: boolean;\n error: unknown | null;\n remove: () => Promise<void>;\n};\n\nexport type KeepItemCardProps<TMeta = Record<string, unknown>> = Omit<\n HTMLAttributes<HTMLElement>,\n \"children\" | \"title\"\n> & {\n item: KeepItem<TMeta>;\n title?: ReactNode | ((item: KeepItem<TMeta>) => ReactNode);\n getImageUrl?: (item: KeepItem<TMeta>) => string | undefined;\n imageAlt?: string;\n render?: RenderProp<KeepItemCardState<TMeta>>;\n children?: ReactNode | RenderProp<KeepItemCardState<TMeta>>;\n removeLabel?: string;\n onRemoveError?: (error: unknown) => void;\n onRemoved?: (item: KeepItem<TMeta>) => void;\n showSaveButton?: boolean;\n saveButtonLabels?: KeepButtonLabels;\n asChild?: boolean;\n};\n\n/** A low-dependency item presentation primitive. The default markup can be replaced with render props. */\nexport function KeepItemCard<TMeta = Record<string, unknown>>({\n item,\n title,\n getImageUrl,\n imageAlt,\n render,\n children,\n removeLabel = \"Remove\",\n onRemoveError,\n onRemoved,\n showSaveButton = true,\n saveButtonLabels,\n asChild = false,\n className,\n ...rootProps\n}: KeepItemCardProps<TMeta>) {\n const itemState = useKeepItem<TMeta>(item.id);\n const contentChildren = asChild && isValidElement(children) ? undefined : children;\n const state: KeepItemCardState<TMeta> = {\n item,\n isSaved: itemState.isSaved,\n isMutating: itemState.isMutating,\n error: itemState.error,\n remove: itemState.remove,\n };\n const resolvedTitle = typeof title === \"function\" ? title(item) : (title ?? getMetaTitle(item.meta) ?? item.id);\n const imageUrl = getImageUrl?.(item);\n\n async function handleRemove() {\n try {\n await itemState.remove();\n onRemoved?.(item);\n } catch (error) {\n onRemoveError?.(error);\n }\n }\n\n const body = render\n ? render(state)\n : typeof contentChildren === \"function\"\n ? contentChildren(state)\n : (contentChildren ?? (\n <>\n {imageUrl ? <img src={imageUrl} alt={imageAlt ?? String(resolvedTitle)} /> : null}\n <h3>{resolvedTitle}</h3>\n {showSaveButton ? (\n <KeepButton\n item={toKeepButtonItem(item)}\n labels={saveButtonLabels}\n getAriaLabel={(buttonState) => `${buttonState.isSaved ? \"Remove\" : \"Save\"} ${String(resolvedTitle)}`}\n />\n ) : null}\n <button type=\"button\" onClick={() => void handleRemove()} disabled={itemState.isMutating}>\n {removeLabel}\n </button>\n </>\n ));\n\n return renderRoot(\n asChild,\n isValidElement(children) ? children : undefined,\n { ...rootProps, className, \"aria-busy\": itemState.isMutating || rootProps[\"aria-busy\"] },\n body,\n \"KeepItemCard\",\n );\n}\n\nexport type KeepListState<TMeta = Record<string, unknown>> = UseKeepListResult<TMeta>;\n\nexport type KeepListProps<TMeta = Record<string, unknown>> = Omit<HTMLAttributes<HTMLElement>, \"children\"> & {\n options?: KeepListOptions<TMeta>;\n children?: ReactNode | RenderProp<KeepListState<TMeta>>;\n renderItem?: (item: KeepItem<TMeta>, state: KeepListState<TMeta>) => ReactNode;\n loading?: ReactNode | RenderProp<KeepListState<TMeta>>;\n empty?: ReactNode | RenderProp<KeepListState<TMeta>>;\n error?: ReactNode | RenderProp<KeepListState<TMeta>>;\n itemCardProps?: Omit<KeepItemCardProps<TMeta>, \"item\" | \"children\" | \"render\">;\n asChild?: boolean;\n};\n\n/** A list primitive with built-in loading, empty, error, and removal states. */\nexport function KeepList<TMeta = Record<string, unknown>>({\n options,\n children,\n renderItem,\n loading = \"Loading saved items…\",\n empty = \"No saved items.\",\n error: errorContent = \"Could not load saved items.\",\n itemCardProps,\n asChild = false,\n className,\n ...rootProps\n}: KeepListProps<TMeta>) {\n const state = useKeepList<TMeta>(options);\n const body = getListBody(state, { children, renderItem, loading, empty, error: errorContent, itemCardProps });\n const root = asChild && isValidElement(children) ? children : undefined;\n return renderRoot(\n asChild,\n root,\n { ...rootProps, className, \"aria-busy\": state.isLoading || rootProps[\"aria-busy\"] },\n body,\n \"KeepList\",\n );\n}\n\nfunction getListBody<TMeta>(\n state: KeepListState<TMeta>,\n options: {\n children?: ReactNode | RenderProp<KeepListState<TMeta>>;\n renderItem?: (item: KeepItem<TMeta>, state: KeepListState<TMeta>) => ReactNode;\n loading: ReactNode | RenderProp<KeepListState<TMeta>>;\n empty: ReactNode | RenderProp<KeepListState<TMeta>>;\n error: ReactNode | RenderProp<KeepListState<TMeta>>;\n itemCardProps?: Omit<KeepItemCardProps<TMeta>, \"item\" | \"children\" | \"render\">;\n },\n): ReactNode {\n if (state.error && state.items.length === 0) return resolveContent(options.error, state);\n if (state.isLoading && !state.isHydrated) return resolveContent(options.loading, state);\n if (state.isHydrated && state.items.length === 0) return resolveContent(options.empty, state);\n if (typeof options.children === \"function\") return options.children(state);\n if (options.children !== undefined && !isValidElement(options.children)) return options.children;\n const items = state.items.map((item) =>\n options.renderItem ? (\n options.renderItem(item, state)\n ) : (\n <li key={item.id}>\n <KeepItemCard item={item} {...options.itemCardProps} />\n </li>\n ),\n );\n return <ul>{items}</ul>;\n}\n\nexport type KeepTagFilterState = {\n tags: string[];\n tagCounts: Record<string, number>;\n value?: string;\n select: (tag?: string) => void;\n};\n\nexport type KeepTagFilterProps<TMeta = Record<string, unknown>> = Omit<\n HTMLAttributes<HTMLElement>,\n \"children\" | \"onChange\"\n> & {\n listOptions?: KeepListOptions<TMeta>;\n value?: string;\n defaultValue?: string;\n onChange?: (tag?: string) => void;\n onValueChange?: (tag?: string) => void;\n allLabel?: ReactNode;\n ariaLabel?: string;\n renderTag?: (tag: string, count: number, selected: boolean) => ReactNode;\n render?: RenderProp<KeepTagFilterState>;\n children?: ReactNode | RenderProp<KeepTagFilterState>;\n asChild?: boolean;\n};\n\n/** An ARIA button group for tag filtering; consumers can connect value to KeepList options. */\nexport function KeepTagFilter<TMeta = Record<string, unknown>>({\n listOptions,\n value: controlledValue,\n defaultValue,\n onChange,\n onValueChange,\n allLabel = \"All\",\n ariaLabel = \"Filter saved items by tag\",\n renderTag,\n render,\n children,\n asChild = false,\n className,\n ...rootProps\n}: KeepTagFilterProps<TMeta>) {\n const list = useKeepList<TMeta>(listOptions);\n const contentChildren = asChild && isValidElement(children) ? undefined : children;\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = controlledValue ?? uncontrolledValue;\n const select = useCallback(\n (tag?: string) => {\n if (controlledValue === undefined) setUncontrolledValue(tag);\n onChange?.(tag);\n onValueChange?.(tag);\n },\n [controlledValue, onChange, onValueChange],\n );\n const state = useMemo<KeepTagFilterState>(\n () => ({ tags: list.tags, tagCounts: list.tagCounts, value, select }),\n [list.tagCounts, list.tags, select, value],\n );\n const body = render\n ? render(state)\n : typeof contentChildren === \"function\"\n ? contentChildren(state)\n : (contentChildren ?? (\n <fieldset>\n <legend>{ariaLabel}</legend>\n <button type=\"button\" aria-pressed={value === undefined} onClick={() => select()}>\n {allLabel}\n </button>\n {list.tags.map((tag) => (\n <button key={tag} type=\"button\" aria-pressed={value === tag} onClick={() => select(tag)}>\n {renderTag ? renderTag(tag, list.tagCounts[tag] ?? 0, value === tag) : tag}\n <span> ({list.tagCounts[tag] ?? 0})</span>\n </button>\n ))}\n </fieldset>\n ));\n return renderRoot(\n asChild,\n isValidElement(children) ? children : undefined,\n { ...rootProps, className },\n body,\n \"KeepTagFilter\",\n );\n}\n\nexport type KeepNoteEditorState<TMeta = Record<string, unknown>> = {\n item: KeepItem<TMeta>;\n note: string;\n setNote: (note: string) => void;\n isDirty: boolean;\n isSaving: boolean;\n error: unknown | null;\n save: () => Promise<void>;\n};\n\nexport type KeepNoteEditorProps<TMeta = Record<string, unknown>> = Omit<\n FormHTMLAttributes<HTMLFormElement>,\n \"children\" | \"onSubmit\"\n> & {\n item: KeepItem<TMeta>;\n label?: ReactNode;\n saveLabel?: ReactNode;\n placeholder?: string;\n onSaved?: (note?: string) => void;\n onSaveError?: (error: unknown) => void;\n render?: RenderProp<KeepNoteEditorState<TMeta>>;\n children?: ReactNode | RenderProp<KeepNoteEditorState<TMeta>>;\n asChild?: boolean;\n};\n\n/** A controlled-by-default note editor that persists through useKeepItem. */\nexport function KeepNoteEditor<TMeta = Record<string, unknown>>({\n item,\n label = \"Note\",\n saveLabel = \"Save note\",\n placeholder,\n onSaved,\n onSaveError,\n render,\n children,\n asChild = false,\n className,\n ...formProps\n}: KeepNoteEditorProps<TMeta>) {\n const itemState = useKeepItem<TMeta>(item.id);\n const contentChildren = asChild && isValidElement(children) ? undefined : children;\n const [note, setNote] = useState(item.note ?? \"\");\n useEffect(() => setNote(itemState.item?.note ?? item.note ?? \"\"), [item.note, itemState.item?.note]);\n const save = useCallback(async () => {\n const nextNote = note.trim() || undefined;\n try {\n await itemState.updateNote(nextNote);\n onSaved?.(nextNote);\n } catch (error) {\n onSaveError?.(error);\n throw error;\n }\n }, [itemState, note, onSaveError, onSaved]);\n const state: KeepNoteEditorState<TMeta> = {\n item,\n note,\n setNote,\n isDirty: note !== (itemState.item?.note ?? item.note ?? \"\"),\n isSaving: itemState.isMutating,\n error: itemState.error,\n save,\n };\n const body = render\n ? render(state)\n : typeof contentChildren === \"function\"\n ? contentChildren(state)\n : (contentChildren ?? (\n <>\n <label>\n {label}\n <textarea\n value={note}\n onChange={(event) => setNote(event.currentTarget.value)}\n placeholder={placeholder}\n disabled={itemState.isMutating}\n />\n </label>\n <button type=\"submit\" disabled={itemState.isMutating} aria-busy={itemState.isMutating}>\n {saveLabel}\n </button>\n </>\n ));\n const handleSubmit: FormHTMLAttributes<HTMLFormElement>[\"onSubmit\"] = (event) => {\n event.preventDefault();\n void save().catch(() => undefined);\n };\n return renderRoot(\n asChild,\n isValidElement(children) ? children : undefined,\n { ...formProps, className, onSubmit: handleSubmit, \"aria-busy\": itemState.isMutating || formProps[\"aria-busy\"] },\n body,\n \"KeepNoteEditor\",\n );\n}\n\nexport type KeepEmptyStateProps = Omit<HTMLAttributes<HTMLElement>, \"children\" | \"title\"> & {\n title?: ReactNode;\n description?: ReactNode;\n action?: ReactNode;\n children?: ReactNode;\n asChild?: boolean;\n};\n\n/** A style-free, reusable empty collection state. */\nexport function KeepEmptyState({\n title = \"No saved items\",\n description,\n action,\n children,\n asChild = false,\n className,\n ...rootProps\n}: KeepEmptyStateProps) {\n const contentChildren = asChild && isValidElement(children) ? undefined : children;\n const body = contentChildren ?? (\n <>\n <h2>{title}</h2>\n {description ? <p>{description}</p> : null}\n {action}\n </>\n );\n return renderRoot(asChild, children, { ...rootProps, className }, body, \"KeepEmptyState\");\n}\n\nexport type KeepStatusValue = \"idle\" | \"empty\" | \"loading\" | \"saving\" | \"syncing\" | \"error\";\n\nexport type KeepStatusLabels = Partial<Record<KeepStatusValue, ReactNode>>;\n\nexport type KeepStatusState<TMeta = Record<string, unknown>> = {\n status: KeepStatusValue;\n error: unknown | null;\n pendingCount: number;\n items: KeepItem<TMeta>[];\n};\n\nexport type KeepStatusProps<TMeta = Record<string, unknown>> = Omit<HTMLAttributes<HTMLElement>, \"children\"> & {\n status?: KeepStatusValue;\n labels?: KeepStatusLabels;\n children?: ReactNode | RenderProp<KeepStatusState<TMeta>>;\n render?: RenderProp<KeepStatusState<TMeta>>;\n asChild?: boolean;\n};\n\n/** Presents provider loading, mutation, synchronization, empty, and error states accessibly. */\nexport function KeepStatus<TMeta = Record<string, unknown>>({\n status,\n labels,\n children,\n render,\n asChild = false,\n className,\n ...rootProps\n}: KeepStatusProps<TMeta>) {\n const context = useKeepContext<TMeta>();\n const contentChildren = asChild && isValidElement(children) ? undefined : children;\n const resolvedStatus = status ?? getDerivedStatus(context);\n const state: KeepStatusState<TMeta> = {\n status: resolvedStatus,\n error: context.error,\n pendingCount: context.syncState.pendingCount,\n items: context.items,\n };\n const body = render\n ? render(state)\n : typeof contentChildren === \"function\"\n ? contentChildren(state)\n : (contentChildren ?? labels?.[resolvedStatus] ?? getDefaultStatusLabel(resolvedStatus));\n const role = rootProps.role ?? (resolvedStatus === \"error\" ? \"alert\" : \"status\");\n return renderRoot(\n asChild,\n isValidElement(children) ? children : undefined,\n { ...rootProps, className, role, \"aria-live\": rootProps[\"aria-live\"] ?? \"polite\" },\n body,\n \"KeepStatus\",\n );\n}\n\nfunction getDerivedStatus<TMeta>(context: ReturnType<typeof useKeepContext<TMeta>>): KeepStatusValue {\n if (context.error) return \"error\";\n if (context.syncState.status === \"pending\" || context.syncState.status === \"syncing\") return \"syncing\";\n if (context.isMutating) return \"saving\";\n if (context.isLoading && !context.isHydrated) return \"loading\";\n if (context.isHydrated && context.items.length === 0) return \"empty\";\n return \"idle\";\n}\n\nfunction getDefaultStatusLabel(status: KeepStatusValue): string {\n if (status === \"empty\") return \"No saved items.\";\n if (status === \"loading\") return \"Loading saved items…\";\n if (status === \"saving\") return \"Saving…\";\n if (status === \"syncing\") return \"Syncing…\";\n if (status === \"error\") return \"Something went wrong.\";\n return \"\";\n}\n\nfunction resolveContent<TState>(content: ReactNode | RenderProp<TState>, state: TState): ReactNode {\n return typeof content === \"function\" ? content(state) : content;\n}\n\nfunction toKeepButtonItem<TMeta>(item: KeepItem<TMeta>): KeepItemInput<TMeta> & { id: string } {\n return {\n id: item.id,\n meta: item.meta,\n targetType: item.targetType,\n note: item.note,\n tags: item.tags,\n };\n}\n\nfunction getMetaTitle<TMeta>(meta: TMeta): string | undefined {\n if (typeof meta !== \"object\" || meta === null || !(\"title\" in meta)) return undefined;\n const title = (meta as { title?: unknown }).title;\n return typeof title === \"string\" && title.trim() ? title.trim() : undefined;\n}\n\nfunction renderRoot(\n asChild: boolean,\n child: ReactNode,\n props: HTMLAttributes<HTMLElement> & Record<string, unknown>,\n body: ReactNode,\n componentName: string,\n): ReactElement {\n if (asChild) {\n if (!isValidElement(child)) throw new Error(`${componentName} with asChild requires a single React element child.`);\n return cloneElement(child as ReactElement<Record<string, unknown>>, { ...props, children: body });\n }\n return <div {...props}>{body}</div>;\n}\n"],"mappings":"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","names":["useKeepContext","useKeepItem","useKeepList","sharedProps","statefulProps"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@keepkit/ui",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Headless, accessible React UI components for KeepKit.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/nitta-a/keepkit.git",
|
|
10
|
+
"directory": "packages/keepkit-ui"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./react": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@keepkit/core": ">=0.2.0",
|
|
31
|
+
"react": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@keepkit/core": "0.3.0",
|
|
35
|
+
"@types/react": "^19.2.18",
|
|
36
|
+
"react": "^19.2.8",
|
|
37
|
+
"tsup": "^8.5.1",
|
|
38
|
+
"typescript": "^6.0.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"dev": "tsup --watch",
|
|
44
|
+
"lint": "tsc --noEmit",
|
|
45
|
+
"test": "node --experimental-strip-types --test test/*.test.ts",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|