@bouku/core 0.0.1

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.
Files changed (112) hide show
  1. package/dist/assets/icons/chevron-left.svg +6 -0
  2. package/dist/assets/icons/chevron-right.svg +6 -0
  3. package/dist/assets/icons/copy.svg +6 -0
  4. package/dist/assets/icons/spinner.svg +6 -0
  5. package/dist/assets/styles/button.css +76 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +18 -0
  8. package/dist/ui/button/_.d.ts +2 -0
  9. package/dist/ui/button/_.js +10 -0
  10. package/dist/ui/button/copy.d.ts +6 -0
  11. package/dist/ui/button/copy.js +16 -0
  12. package/dist/ui/button/normal.d.ts +11 -0
  13. package/dist/ui/button/normal.js +48 -0
  14. package/dist/ui/date/_.d.ts +1 -0
  15. package/dist/ui/date/_.js +8 -0
  16. package/dist/ui/date/time-ago.d.ts +7 -0
  17. package/dist/ui/date/time-ago.js +13 -0
  18. package/dist/ui/dropdown/normal.d.ts +10 -0
  19. package/dist/ui/dropdown/normal.js +22 -0
  20. package/dist/ui/fade/_.d.ts +1 -0
  21. package/dist/ui/fade/_.js +8 -0
  22. package/dist/ui/fade/circle.d.ts +10 -0
  23. package/dist/ui/fade/circle.js +8 -0
  24. package/dist/ui/heading/_.d.ts +2 -0
  25. package/dist/ui/heading/_.js +10 -0
  26. package/dist/ui/heading/normal.d.ts +24 -0
  27. package/dist/ui/heading/normal.js +26 -0
  28. package/dist/ui/heading/page.d.ts +25 -0
  29. package/dist/ui/heading/page.js +26 -0
  30. package/dist/ui/index.d.ts +19 -0
  31. package/dist/ui/index.js +45 -0
  32. package/dist/ui/information/_.d.ts +1 -0
  33. package/dist/ui/information/_.js +8 -0
  34. package/dist/ui/information/card.d.ts +7 -0
  35. package/dist/ui/information/card.js +20 -0
  36. package/dist/ui/layout/_.d.ts +2 -0
  37. package/dist/ui/layout/_.js +10 -0
  38. package/dist/ui/layout/absolute.d.ts +9 -0
  39. package/dist/ui/layout/absolute.js +12 -0
  40. package/dist/ui/layout/fade.d.ts +7 -0
  41. package/dist/ui/layout/fade.js +20 -0
  42. package/dist/ui/layout/flex.d.ts +17 -0
  43. package/dist/ui/layout/flex.js +49 -0
  44. package/dist/ui/layout/footer.d.ts +15 -0
  45. package/dist/ui/layout/footer.js +13 -0
  46. package/dist/ui/layout/media-row.d.ts +15 -0
  47. package/dist/ui/layout/media-row.js +11 -0
  48. package/dist/ui/layout/rnd.d.ts +10 -0
  49. package/dist/ui/layout/rnd.js +32 -0
  50. package/dist/ui/layout/scroll-box.d.ts +14 -0
  51. package/dist/ui/layout/scroll-box.js +11 -0
  52. package/dist/ui/layout/section.d.ts +15 -0
  53. package/dist/ui/layout/section.js +19 -0
  54. package/dist/ui/layout/separator.d.ts +3 -0
  55. package/dist/ui/layout/separator.js +8 -0
  56. package/dist/ui/link/_.d.ts +1 -0
  57. package/dist/ui/link/_.js +8 -0
  58. package/dist/ui/link/tab.d.ts +22 -0
  59. package/dist/ui/link/tab.js +30 -0
  60. package/dist/ui/list/index.d.ts +2 -0
  61. package/dist/ui/list/index.js +10 -0
  62. package/dist/ui/list/item.d.ts +9 -0
  63. package/dist/ui/list/item.js +9 -0
  64. package/dist/ui/list/search-bar.d.ts +14 -0
  65. package/dist/ui/list/search-bar.js +33 -0
  66. package/dist/ui/list/variants/bullet.d.ts +9 -0
  67. package/dist/ui/list/variants/bullet.js +21 -0
  68. package/dist/ui/list/variants/number.d.ts +10 -0
  69. package/dist/ui/list/variants/number.js +23 -0
  70. package/dist/ui/pagination/index.d.ts +9 -0
  71. package/dist/ui/pagination/index.js +21 -0
  72. package/dist/ui/progress/_.d.ts +1 -0
  73. package/dist/ui/progress/_.js +8 -0
  74. package/dist/ui/progress/bar.d.ts +4 -0
  75. package/dist/ui/progress/bar.js +12 -0
  76. package/dist/ui/table/_.d.ts +1 -0
  77. package/dist/ui/table/_.js +8 -0
  78. package/dist/ui/table/normal.d.ts +7 -0
  79. package/dist/ui/table/normal.js +20 -0
  80. package/dist/ui/text/_.d.ts +2 -0
  81. package/dist/ui/text/_.js +10 -0
  82. package/dist/ui/text/badge.d.ts +18 -0
  83. package/dist/ui/text/badge.js +42 -0
  84. package/dist/ui/text/input.d.ts +39 -0
  85. package/dist/ui/text/input.js +50 -0
  86. package/dist/utils/functions/_.d.ts +2 -0
  87. package/dist/utils/functions/_.js +18 -0
  88. package/dist/utils/functions/element.d.ts +3 -0
  89. package/dist/utils/functions/element.js +7 -0
  90. package/dist/utils/functions/text.d.ts +2 -0
  91. package/dist/utils/functions/text.js +36 -0
  92. package/dist/utils/hooks/_.d.ts +5 -0
  93. package/dist/utils/hooks/_.js +21 -0
  94. package/dist/utils/hooks/clock.d.ts +8 -0
  95. package/dist/utils/hooks/clock.js +28 -0
  96. package/dist/utils/hooks/element.d.ts +13 -0
  97. package/dist/utils/hooks/element.js +27 -0
  98. package/dist/utils/hooks/pagination.d.ts +36 -0
  99. package/dist/utils/hooks/pagination.js +82 -0
  100. package/dist/utils/hooks/router.d.ts +5 -0
  101. package/dist/utils/hooks/router.js +15 -0
  102. package/dist/utils/hooks/state.d.ts +26 -0
  103. package/dist/utils/hooks/state.js +48 -0
  104. package/dist/utils/index.d.ts +3 -0
  105. package/dist/utils/index.js +19 -0
  106. package/dist/utils/types/_.d.ts +2 -0
  107. package/dist/utils/types/_.js +18 -0
  108. package/dist/utils/types/element.d.ts +20 -0
  109. package/dist/utils/types/element.js +3 -0
  110. package/dist/utils/types/state.d.ts +2 -0
  111. package/dist/utils/types/state.js +2 -0
  112. package/package.json +42 -0
@@ -0,0 +1,18 @@
1
+ import type { Component } from "../../utils";
2
+ type Props = Component & {
3
+ color?: string;
4
+ icon?: React.ReactNode;
5
+ size?: "xs" | "sm" | "md";
6
+ };
7
+ /**
8
+ * Color-themed badge component.
9
+ *
10
+ * To use a CSS variable it must exist and be passed in the
11
+ * form `--variable-name`. Any other format may cause unexpected
12
+ * results. CSS variables use 'color-mix' which is not supported in
13
+ * some old browsers such as Safari 15.3 and Internet Explorer.
14
+ *
15
+ * @param {string} color - Color of badge. (optional, default = accent)
16
+ **/
17
+ export declare function Badge({ style, color, size, icon, children }: Props): import("react").JSX.Element | null;
18
+ export default Badge;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Badge = Badge;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const style_1 = require("@bouko/style");
6
+ const tailwind_variants_1 = require("tailwind-variants");
7
+ /**
8
+ * Color-themed badge component.
9
+ *
10
+ * To use a CSS variable it must exist and be passed in the
11
+ * form `--variable-name`. Any other format may cause unexpected
12
+ * results. CSS variables use 'color-mix' which is not supported in
13
+ * some old browsers such as Safari 15.3 and Internet Explorer.
14
+ *
15
+ * @param {string} color - Color of badge. (optional, default = accent)
16
+ **/
17
+ function Badge({ style, color = "--color-accent", size = "md", icon, children }) {
18
+ if (!children)
19
+ return null;
20
+ if (color === "future")
21
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 test-badge", children: [icon && (0, jsx_runtime_1.jsx)("div", { className: "text-primary-dark", children: icon }), (0, jsx_runtime_1.jsx)("span", { children: children })] }));
22
+ return ((0, jsx_runtime_1.jsx)("span", { className: (0, style_1.cn)(base({ size }), style), style: styles(color), children: children }));
23
+ }
24
+ const styles = (color) => ({
25
+ background: (0, style_1.opacitize)(color),
26
+ border: `1px solid var(${color})`,
27
+ borderRadius: "calc(infinity * 1px)",
28
+ textAlign: "center",
29
+ fontWeight: "600",
30
+ color: "var(--color-primary)"
31
+ });
32
+ const base = (0, tailwind_variants_1.tv)({
33
+ base: "w-min",
34
+ variants: {
35
+ size: {
36
+ xs: "px-1 py-px text-[0.5rem]",
37
+ sm: "px-1 py-px text-xs",
38
+ md: "px-2 py-1 text-sm"
39
+ }
40
+ }
41
+ });
42
+ exports.default = Badge;
@@ -0,0 +1,39 @@
1
+ import { ReactNode } from "react";
2
+ type FieldProps<T> = {
3
+ id?: string;
4
+ label?: string;
5
+ style?: string;
6
+ value?: T;
7
+ update?: (x: T) => void;
8
+ updateForm?: (id: string, x: T) => void;
9
+ required?: boolean;
10
+ disabled?: boolean;
11
+ note?: ReactNode;
12
+ };
13
+ type Variant = "normal" | "ghost";
14
+ type Props<T> = FieldProps<T> & {
15
+ type?: "text" | "number" | "email" | "password";
16
+ variant?: Variant;
17
+ style?: string;
18
+ placeholder?: string;
19
+ onEnter?: (x: string) => void;
20
+ };
21
+ /**
22
+ * Text field component.
23
+ *
24
+ * Wraps the native `input` element with custom styling and validation.
25
+ * It displays an optional label and note, and can be marked as required.
26
+ * Optional callback for when the user presses `Enter` key.
27
+ * The value is controlled externally via props, usually `useState`.
28
+ *
29
+ * Variants:
30
+ * - `normal`: Standard input with primary color scheme.
31
+ * - `ghost`: Transparent for injecting input inside other component.
32
+ *
33
+ * @param {Variant} variant - Visual style variant. (optional, default = normal)
34
+ * @param {string} style - Additional class names for styling. (optional)
35
+ * @param {string} placeholder - Placeholder text for the input. (optional)
36
+ * @param {Function} onEnter - Callback when `Enter` key is pressed. (optional)
37
+ **/
38
+ export default function Input<T>({ id, type, variant, style, value, update, updateForm, onEnter, disabled, placeholder, ...props }: Props<T>): import("react").JSX.Element;
39
+ export {};
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = Input;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const style_1 = require("@bouko/style");
6
+ const tailwind_variants_1 = require("tailwind-variants");
7
+ function Field({ style, label, required = true, note, children }) {
8
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, style_1.cn)(fieldStyles.container, style), children: [label && (0, jsx_runtime_1.jsxs)("span", { className: fieldStyles.label, children: [label, " ", !required ? (0, jsx_runtime_1.jsx)("span", { className: "italic text-slate-400", children: "(optional)" }) : ""] }), children, note && (0, jsx_runtime_1.jsx)("span", { className: fieldStyles.note, children: note })] }));
9
+ }
10
+ const fieldStyles = {
11
+ container: "flex flex-col shrink-0 gap-1 overflow-hidden",
12
+ label: "text-xs text-primary-dark",
13
+ note: "mt-1 text-xs text-slate-500 whitespace-pre-line"
14
+ };
15
+ /**
16
+ * Text field component.
17
+ *
18
+ * Wraps the native `input` element with custom styling and validation.
19
+ * It displays an optional label and note, and can be marked as required.
20
+ * Optional callback for when the user presses `Enter` key.
21
+ * The value is controlled externally via props, usually `useState`.
22
+ *
23
+ * Variants:
24
+ * - `normal`: Standard input with primary color scheme.
25
+ * - `ghost`: Transparent for injecting input inside other component.
26
+ *
27
+ * @param {Variant} variant - Visual style variant. (optional, default = normal)
28
+ * @param {string} style - Additional class names for styling. (optional)
29
+ * @param {string} placeholder - Placeholder text for the input. (optional)
30
+ * @param {Function} onEnter - Callback when `Enter` key is pressed. (optional)
31
+ **/
32
+ function Input({ id, type = "text", variant, style, value, update, updateForm, onEnter, disabled, placeholder, ...props }) {
33
+ const handleUpdate = (value) => {
34
+ if (id && updateForm)
35
+ updateForm(id, value);
36
+ else if (update)
37
+ update(value);
38
+ };
39
+ return ((0, jsx_runtime_1.jsx)(Field, { ...props, children: (0, jsx_runtime_1.jsx)("input", { type: type, className: (0, style_1.cn)(styles({ variant }), style), placeholder: placeholder || `Enter ${id}`, value: value, onChange: ({ target: { value } }) => handleUpdate(value), onKeyUp: ({ key }) => key === "Enter" && value ? onEnter?.(value) : null, disabled: disabled }) }));
40
+ }
41
+ const styles = (0, tailwind_variants_1.tv)({
42
+ base: "px-3 py-2 duration-200 rounded text-xs sm:text-sm placeholder:text-primary-darker text-primary disabled:brightness-90 disabled:cursor-not-allowed",
43
+ defaultVariants: { variant: "normal" },
44
+ variants: {
45
+ variant: {
46
+ normal: "bg-input border border-outline focus:border-outline-light outline-none",
47
+ ghost: "p-0 bg-transparent border-none outline-none"
48
+ }
49
+ }
50
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./element";
2
+ export * from "./text";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./element"), exports);
18
+ __exportStar(require("./text"), exports);
@@ -0,0 +1,3 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ import type { ListItem } from "../types/_";
3
+ export declare function renderList<T extends object>(items: T[], Component: (props: T & ListItem) => ReactElement): ReactNode;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderList = renderList;
4
+ const react_1 = require("react");
5
+ function renderList(items, Component) {
6
+ return items.map((item, index) => ((0, react_1.createElement)(Component, { ...item, index: index, key: index })));
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function formatText(text: string): ReactNode;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatText = formatText;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ts_1 = require("@bouko/ts");
6
+ const delimiterConfig = [
7
+ {
8
+ key: "**",
9
+ style: "font-bold",
10
+ },
11
+ {
12
+ key: "~~",
13
+ style: "font-semibold",
14
+ },
15
+ {
16
+ key: "^^",
17
+ style: "text-accent",
18
+ },
19
+ ];
20
+ const delimiterPattern = delimiterConfig.map(({ key }) => {
21
+ const escaped = (0, ts_1.escapeRegex)(key);
22
+ return `${escaped}.*?${escaped}`;
23
+ }).join("|");
24
+ const delimiterRegex = new RegExp(`(${delimiterPattern})`, "g");
25
+ function formatText(text) {
26
+ const parts = text
27
+ .split(delimiterRegex)
28
+ .filter(Boolean);
29
+ return (0, jsx_runtime_1.jsx)("span", { children: parts.map((part, index) => {
30
+ const delimiter = delimiterConfig.find(({ key }) => part.startsWith(key) && part.endsWith(key));
31
+ if (!delimiter)
32
+ return part;
33
+ const content = part.slice(delimiter.key.length, -delimiter.key.length);
34
+ return ((0, jsx_runtime_1.jsx)("span", { className: delimiter.style, children: content }, index));
35
+ }) });
36
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./clock";
2
+ export * from "./element";
3
+ export * from "./pagination";
4
+ export * from "./router";
5
+ export * from "./state";
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./clock"), exports);
18
+ __exportStar(require("./element"), exports);
19
+ __exportStar(require("./pagination"), exports);
20
+ __exportStar(require("./router"), exports);
21
+ __exportStar(require("./state"), exports);
@@ -0,0 +1,8 @@
1
+ type Props = {
2
+ action: () => Promise<boolean | void>;
3
+ duration: number;
4
+ deps: unknown[];
5
+ oops?: (x: string) => void;
6
+ };
7
+ export default function useInterval({ action, duration, deps, oops }: Props): void;
8
+ export {};
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.default = useInterval;
5
+ const react_1 = require("react");
6
+ function useInterval({ action, duration, deps = [], oops }) {
7
+ const ref = (0, react_1.useRef)(async () => false);
8
+ (0, react_1.useEffect)(() => {
9
+ ref.current = action;
10
+ }, [action]);
11
+ (0, react_1.useEffect)(() => {
12
+ if (deps.some(v => v === undefined || v === null))
13
+ return;
14
+ const tick = async () => {
15
+ try {
16
+ const stop = await ref.current();
17
+ if (stop && id)
18
+ clearInterval(id);
19
+ }
20
+ catch (err) {
21
+ oops?.(err.message);
22
+ }
23
+ };
24
+ const id = setInterval(tick, duration);
25
+ tick();
26
+ return () => clearInterval(id);
27
+ }, [duration, ...deps]);
28
+ }
@@ -0,0 +1,13 @@
1
+ import { type RefObject } from "react";
2
+ type Options = {
3
+ onResize?: () => void;
4
+ };
5
+ export type DivRef = RefObject<HTMLDivElement | null>;
6
+ export type AudioRef = RefObject<HTMLAudioElement | null>;
7
+ export declare function withRef<T>(ref: RefObject<T | null>, fn: (el: T) => void): void;
8
+ export declare const useElement: (init?: HTMLDivElement, opts?: Options) => {
9
+ ref: RefObject<HTMLDivElement | null>;
10
+ container: HTMLElement | null | undefined;
11
+ width: number;
12
+ };
13
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useElement = void 0;
4
+ exports.withRef = withRef;
5
+ const react_1 = require("react");
6
+ function withRef(ref, fn) {
7
+ const el = ref.current;
8
+ if (!el)
9
+ return;
10
+ return fn(el);
11
+ }
12
+ const useElement = (init, opts = {}) => {
13
+ const ref = (0, react_1.useRef)(init ?? null);
14
+ const element = ref.current;
15
+ const container = element?.parentElement;
16
+ const width = element?.clientWidth || 0;
17
+ (0, react_1.useEffect)(() => withRef(ref, (el => {
18
+ if (!opts?.onResize)
19
+ return;
20
+ const observer = new ResizeObserver(opts.onResize);
21
+ observer.observe(el);
22
+ opts.onResize();
23
+ return () => observer.disconnect();
24
+ })), [element, opts?.onResize]);
25
+ return { ref, container, width };
26
+ };
27
+ exports.useElement = useElement;
@@ -0,0 +1,36 @@
1
+ type Options<T> = {
2
+ fetcher: (query: PageQuery) => Promise<T[] | {
3
+ items: T[];
4
+ total?: number;
5
+ totalPages?: number;
6
+ }>;
7
+ pageSize: number;
8
+ };
9
+ export type PageQuery = {
10
+ page: number;
11
+ pageSize: number;
12
+ refresh?: boolean;
13
+ };
14
+ export type PageState<T> = {
15
+ page: number;
16
+ isLoading: boolean;
17
+ items: T[];
18
+ totalPages: number;
19
+ fetchedAt: Date | null;
20
+ prev: () => void;
21
+ next: () => void;
22
+ jump: (page: number) => void;
23
+ refresh: () => void;
24
+ };
25
+ export declare function usePagination<T>({ fetcher, pageSize }: Options<T>): {
26
+ items: T[];
27
+ isLoading: boolean;
28
+ page: number;
29
+ totalPages: number;
30
+ prev: () => void;
31
+ next: () => void;
32
+ jump: (target: number) => void;
33
+ refresh: () => Promise<void>;
34
+ fetchedAt: Date | null;
35
+ };
36
+ export {};
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePagination = usePagination;
4
+ const react_1 = require("react");
5
+ const react_router_dom_1 = require("react-router-dom");
6
+ function usePagination({ fetcher, pageSize }) {
7
+ const [params, setParams] = (0, react_router_dom_1.useSearchParams)();
8
+ const [items, setItems] = (0, react_1.useState)([]);
9
+ const [totalPages, setTotalPages] = (0, react_1.useState)(0);
10
+ const [loading, setLoading] = (0, react_1.useState)(false);
11
+ const [fetchedAt, setFetchedAt] = (0, react_1.useState)(null);
12
+ const raw = params.get("page");
13
+ const page = Math.max(1, Number(raw) || 1);
14
+ const setPage = (0, react_1.useCallback)((nextPage) => {
15
+ const p = Math.max(1, Math.trunc(nextPage));
16
+ const next = new URLSearchParams(params.toString());
17
+ next.set("page", String(p));
18
+ setParams(next);
19
+ }, [params, setParams]);
20
+ const jump = (0, react_1.useCallback)((target) => setPage(target), [setPage]);
21
+ const fetchPage = (0, react_1.useCallback)(async (refresh = false) => {
22
+ setLoading(true);
23
+ try {
24
+ const result = await fetcher({ page, pageSize, refresh });
25
+ // normalize result to { items, totalPages }
26
+ let resultItems = [];
27
+ let computedTotalPages = 0;
28
+ if (Array.isArray(result)) {
29
+ resultItems = result;
30
+ // can't compute total pages from array alone
31
+ computedTotalPages = 0;
32
+ }
33
+ else {
34
+ resultItems = result?.items ?? [];
35
+ if (typeof result?.totalPages === "number") {
36
+ computedTotalPages = Math.max(0, Math.trunc(result.totalPages));
37
+ }
38
+ else if (typeof result?.total === "number") {
39
+ computedTotalPages = Math.max(0, Math.ceil(result.total / pageSize));
40
+ }
41
+ else {
42
+ computedTotalPages = 0;
43
+ }
44
+ }
45
+ setItems(resultItems);
46
+ setTotalPages(computedTotalPages);
47
+ setFetchedAt(new Date());
48
+ }
49
+ finally {
50
+ setLoading(false);
51
+ }
52
+ }, [page, pageSize]);
53
+ (0, react_1.useEffect)(() => {
54
+ fetchPage(false);
55
+ }, [fetchPage]);
56
+ const prev = (0, react_1.useCallback)(() => {
57
+ if (page > 1)
58
+ setPage(page - 1);
59
+ }, [page, setPage]);
60
+ const next = (0, react_1.useCallback)(() => {
61
+ // if totalPages is known, don't advance past it
62
+ if (totalPages > 0) {
63
+ if (page < totalPages)
64
+ setPage(page + 1);
65
+ }
66
+ else {
67
+ // unknown total -> allow advancing
68
+ setPage(page + 1);
69
+ }
70
+ }, [page, setPage, totalPages]);
71
+ return {
72
+ items,
73
+ isLoading: loading,
74
+ page,
75
+ totalPages,
76
+ prev,
77
+ next,
78
+ jump,
79
+ refresh: () => fetchPage(true),
80
+ fetchedAt,
81
+ };
82
+ }
@@ -0,0 +1,5 @@
1
+ export declare function useRouter<T>(): {
2
+ navigate: import("react-router").NavigateFunction;
3
+ params: T;
4
+ };
5
+ export declare const useSearchParam: (key: string) => string | null;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSearchParam = void 0;
4
+ exports.useRouter = useRouter;
5
+ const react_router_dom_1 = require("react-router-dom");
6
+ function useRouter() {
7
+ const navigate = (0, react_router_dom_1.useNavigate)();
8
+ const params = (0, react_router_dom_1.useParams)();
9
+ return { navigate, params };
10
+ }
11
+ const useSearchParam = (key) => {
12
+ const [params] = (0, react_router_dom_1.useSearchParams)();
13
+ return params.get(key);
14
+ };
15
+ exports.useSearchParam = useSearchParam;
@@ -0,0 +1,26 @@
1
+ type Options<T> = {
2
+ fetcher: () => Promise<T>;
3
+ updater: (x?: T) => Promise<T | null>;
4
+ };
5
+ export declare function useValue<T>({ fetcher, updater }: Options<T>): {
6
+ value: T | null;
7
+ update: (x?: T) => Promise<void>;
8
+ };
9
+ export declare function useList<T extends {
10
+ id?: string;
11
+ }>(initial?: T[]): {
12
+ items: T[];
13
+ add: (entry: T) => void;
14
+ updateOne: (id: string, entry: Partial<T>) => void;
15
+ remove: (id: string) => void;
16
+ clear: () => void;
17
+ };
18
+ type MapOptions<T> = {
19
+ fetcher: () => Promise<Record<string, T> | null>;
20
+ updater: (x: string, value: T) => Promise<T>;
21
+ };
22
+ export declare function useMap<T>({ fetcher, updater }: MapOptions<T>): {
23
+ config: Record<string, T>;
24
+ update: (id: string, x: T) => Promise<void>;
25
+ };
26
+ export {};
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useValue = useValue;
4
+ exports.useList = useList;
5
+ exports.useMap = useMap;
6
+ const react_1 = require("react");
7
+ function useValue({ fetcher, updater }) {
8
+ const [value, setValue] = (0, react_1.useState)(null);
9
+ (0, react_1.useEffect)(() => {
10
+ fetcher()
11
+ .then(setValue);
12
+ }, []);
13
+ const update = async (x) => {
14
+ const res = await updater(x);
15
+ setValue(res || null);
16
+ };
17
+ return { value, update };
18
+ }
19
+ function useList(initial = []) {
20
+ const [items, setItems] = (0, react_1.useState)(initial);
21
+ const add = (entry) => setItems(prev => [...prev, entry]);
22
+ const remove = (id) => setItems(prev => prev.filter(x => x.id !== id));
23
+ const updateOne = (id, entry) => {
24
+ setItems(prev => prev.map((x, i) => x.id === id ? { ...x, ...entry } : x));
25
+ };
26
+ const clear = () => {
27
+ setItems([]);
28
+ };
29
+ return {
30
+ items,
31
+ add,
32
+ updateOne,
33
+ remove,
34
+ clear
35
+ };
36
+ }
37
+ function useMap({ fetcher, updater }) {
38
+ const [config, setConfig] = (0, react_1.useState)({});
39
+ (0, react_1.useEffect)(() => {
40
+ fetcher()
41
+ .then(x => setConfig(x || {}));
42
+ }, []);
43
+ const update = async (id, x) => {
44
+ const res = await updater(id, x);
45
+ setConfig(prev => ({ ...prev, [id]: res }));
46
+ };
47
+ return { config, update };
48
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./types/_";
2
+ export * from "./functions/_";
3
+ export * from "./hooks/_";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types/_"), exports);
18
+ __exportStar(require("./functions/_"), exports);
19
+ __exportStar(require("./hooks/_"), exports);
@@ -0,0 +1,2 @@
1
+ export * from "./element";
2
+ export * from "./state";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./element"), exports);
18
+ __exportStar(require("./state"), exports);
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Common props for React components.
4
+ *
5
+ * @property `Component` - Component with Tailwind style(s) and children.
6
+ * @property `Clickable` - Component that executes a callback when clicked.
7
+ * @property `Hoverable` - Component that tracks hovered state.
8
+ * @property `ListItem` - For items that belong in a list.
9
+ **/
10
+ export type Component = {
11
+ style?: string;
12
+ children?: ReactNode;
13
+ };
14
+ export type Hoverable = {
15
+ onHover?: () => void;
16
+ onLeave?: () => void;
17
+ };
18
+ export type ListItem = {
19
+ index: number;
20
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //
@@ -0,0 +1,2 @@
1
+ import type { Dispatch, SetStateAction } from "react";
2
+ export type SetState<T> = Dispatch<SetStateAction<T>>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });