@protobase/ui 0.15.5 → 0.16.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.
- package/dist/account/two-factor-setup.js +6 -5
- package/dist/app/app.js +1 -1
- package/dist/app/create/create-page.js +1 -0
- package/dist/app/create/create-record-form.js +1 -0
- package/dist/app/current-address.d.ts +5 -1
- package/dist/app/current-address.js +2 -1
- package/dist/app/displays/map-display.js +50 -34
- package/dist/app/displays/map-groups.js +12 -3
- package/dist/app/displays/measured-heights.js +23 -0
- package/dist/app/displays/record-card.js +29 -17
- package/dist/app/displays/virtual-cards.js +12 -14
- package/dist/app/embedded-list/embedded-display.js +7 -3
- package/dist/app/embedded-list/embedded-list.js +5 -4
- package/dist/app/field-editor.js +9 -1
- package/dist/app/list-page.js +5 -3
- package/dist/app/map/approximate-position.js +76 -0
- package/dist/app/map/area-features.js +80 -0
- package/dist/app/map/maplibre.js +11 -0
- package/dist/app/map/position-areas.js +78 -0
- package/dist/app/map/record-map.js +2 -7
- package/dist/app/map/record-position.js +5 -4
- package/dist/app/map/records-map.js +24 -7
- package/dist/app/map/theme-color.js +23 -0
- package/dist/app/nav-recent.js +1 -1
- package/dist/app/pages/blocks/card-frame.js +2 -2
- package/dist/app/pages/blocks/grid.js +10 -2
- package/dist/app/pages/blocks/link.js +1 -1
- package/dist/app/pages/blocks/record-row.js +51 -0
- package/dist/app/pages/blocks/stat.js +9 -5
- package/dist/app/pages/field-value.js +1 -1
- package/dist/app/pages/render-node.js +2 -0
- package/dist/app/record-page.js +5 -1
- package/dist/app/record-row/record-row-body.js +114 -0
- package/dist/app/record-row/record-row.js +93 -0
- package/dist/app/record-row/row-window.js +20 -0
- package/dist/app/record-row/saved-record-row.js +42 -0
- package/dist/app/record-row/use-row-scroll.js +51 -0
- package/dist/app/router.js +7 -8
- package/dist/app/saved/saved-records.js +1 -1
- package/dist/app/search/search-model.js +1 -1
- package/dist/app/searches/filter-chip-list.js +28 -0
- package/dist/app/searches/save-search-button.js +72 -8
- package/dist/app/searches/saved-search-rows.js +73 -0
- package/dist/app/searches/saved-search-section.js +91 -73
- package/dist/app/searches/saved-search.js +7 -3
- package/dist/app/searches/saved-searches.js +3 -5
- package/dist/app/searches/search-controls.js +13 -2
- package/dist/app/searches/search-diff.js +97 -0
- package/dist/app/searches/search-name-dialog.js +62 -0
- package/dist/app/searches/update-search-dialog.js +196 -0
- package/dist/app/searches/use-opened-search.js +15 -0
- package/dist/app/searches/use-save-search.js +68 -21
- package/dist/app/searches/use-search-row.js +56 -0
- package/dist/app/use-record-editor.js +5 -3
- package/dist/app-shell/app-shell.d.ts +1 -0
- package/dist/app-shell/app-shell.js +4 -3
- package/dist/app-shell/page-scroll.js +17 -1
- package/dist/app-shell/sidebar.d.ts +1 -0
- package/dist/app-shell/sidebar.js +4 -4
- package/dist/app-shell/top-bar.js +4 -4
- package/dist/auth/auth-gate.js +18 -9
- package/dist/auth/auth-provider.js +1 -0
- package/dist/auth/code-form.js +77 -0
- package/dist/auth/email-code-form.js +18 -77
- package/dist/auth/forgot-password-form.js +10 -10
- package/dist/auth/forgot-password-page.js +2 -2
- package/dist/auth/resend-code-button.js +29 -0
- package/dist/auth/sign-in-form.js +7 -8
- package/dist/auth/sign-in-page.js +3 -3
- package/dist/auth/sign-in-prompt.js +13 -9
- package/dist/auth/sign-up-code-form.js +22 -0
- package/dist/auth/sign-up-details-form.js +81 -111
- package/dist/auth/sign-up-page.js +17 -8
- package/dist/auth/two-factor-form.js +3 -3
- package/dist/auth/use-sign-in-address.js +21 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/lib/flip-guard.js +43 -0
- package/dist/lib/page-scroll-lock.js +20 -0
- package/dist/lib/use-focus-trap.js +4 -4
- package/dist/organizations/invitation-page.js +7 -6
- package/dist/package.js +1 -1
- package/dist/primitives/dialog.js +3 -2
- package/dist/primitives/otp-input.d.ts +21 -0
- package/dist/primitives/otp-input.js +117 -0
- package/dist/primitives/otp-value.js +23 -0
- package/dist/primitives/responsive-dialog.d.ts +3 -1
- package/dist/primitives/responsive-dialog.js +2 -1
- package/dist/primitives/route-link.d.ts +1 -0
- package/dist/primitives/route-link.js +20 -0
- package/dist/primitives/textarea.js +36 -0
- package/dist/record-view/field.js +56 -53
- package/dist/shell/assets/app-sDEk9LFu.js +66 -0
- package/dist/shell/assets/index-D0AzKE47.css +2 -0
- package/dist/shell/assets/{index-DxmdRnr8.js → index-L7nXyA_7.js} +1 -1
- package/dist/shell/assets/maplibre-AOh9A0NP.js +814 -0
- package/dist/shell/assets/record-map-DbwHSPf3.js +1 -0
- package/dist/shell/assets/records-map-2U_CQ4RG.js +1 -0
- package/dist/shell/assets/shared-protobase-ui-BMcStVNP.js +1 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/styles.css +15 -0
- package/dist/toasts/toast.js +22 -6
- package/package.json +4 -4
- package/dist/auth/code-input.js +0 -17
- package/dist/auth/sign-in-address.js +0 -47
- package/dist/shell/assets/app-BdEV1_xI.js +0 -66
- package/dist/shell/assets/index-ibaHR5wQ.css +0 -2
- package/dist/shell/assets/maplibre-gl-DFcSGTFJ.js +0 -1
- package/dist/shell/assets/record-map-CL9bBLic.js +0 -814
- package/dist/shell/assets/records-map-DgtD0cK5.js +0 -1
- package/dist/shell/assets/shared-protobase-ui-ibIDwOpE.js +0 -1
- /package/dist/shell/assets/{record-map-DBE96hFo.css → maplibre-DBE96hFo.css} +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { editedCode, pastedCode } from "./otp-value.js";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/primitives/otp-input.tsx
|
|
6
|
+
/**
|
|
7
|
+
* A one-time code field drawn as a box per digit: one real input, so the system's code autofill (`one-time-code`, from
|
|
8
|
+
* a mail or a text message), pasting a whole code into any box, the phone's digit keyboard and a form's `required`
|
|
9
|
+
* work as on any field. Arrow keys, Home and End move between boxes, and a digit typed on a filled box replaces it.
|
|
10
|
+
* Label it as any input, with a `<label>` around it or `aria-label`.
|
|
11
|
+
*/
|
|
12
|
+
const OtpInput = ({ value, onChange, onComplete, length = 6, invalid, disabled, className, ref, onFocus, onBlur, onSelect, onKeyDown, onClick, onPaste, ...rest }) => {
|
|
13
|
+
const input = useRef(null);
|
|
14
|
+
const [focused, setFocused] = useState(false);
|
|
15
|
+
const [caret, setCaret] = useState(value.length);
|
|
16
|
+
const code = value.slice(0, length);
|
|
17
|
+
const active = Math.min(caret, length - 1);
|
|
18
|
+
const attach = (node) => {
|
|
19
|
+
input.current = node;
|
|
20
|
+
if (typeof ref === "function") ref(node);
|
|
21
|
+
else if (ref) ref.current = node;
|
|
22
|
+
};
|
|
23
|
+
const commit = (next) => {
|
|
24
|
+
if (next === code) return;
|
|
25
|
+
onChange(next);
|
|
26
|
+
if (next.length === length) onComplete?.(next);
|
|
27
|
+
};
|
|
28
|
+
const place = (at) => {
|
|
29
|
+
const field = input.current;
|
|
30
|
+
if (!field) return;
|
|
31
|
+
const to = Math.max(0, Math.min(at, code.length));
|
|
32
|
+
field.setSelectionRange(to, to < code.length ? to + 1 : to);
|
|
33
|
+
setCaret(to);
|
|
34
|
+
};
|
|
35
|
+
const moveWithKeys = (event) => {
|
|
36
|
+
onKeyDown?.(event);
|
|
37
|
+
if (event.defaultPrevented || event.shiftKey || event.altKey || event.metaKey || event.ctrlKey) return;
|
|
38
|
+
const at = event.currentTarget.selectionStart ?? code.length;
|
|
39
|
+
const target = {
|
|
40
|
+
ArrowLeft: at - 1,
|
|
41
|
+
ArrowRight: at + 1,
|
|
42
|
+
Home: 0,
|
|
43
|
+
End: code.length
|
|
44
|
+
}[event.key];
|
|
45
|
+
if (target === void 0) return;
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
place(target);
|
|
48
|
+
};
|
|
49
|
+
const placeAtPointer = (event) => {
|
|
50
|
+
onClick?.(event);
|
|
51
|
+
const box = event.currentTarget.getBoundingClientRect();
|
|
52
|
+
if (event.clientX <= box.left || event.clientX >= box.right) return;
|
|
53
|
+
place(Math.floor((event.clientX - box.left) / box.width * length));
|
|
54
|
+
};
|
|
55
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
56
|
+
className: cn("relative", disabled && "opacity-50", className),
|
|
57
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
58
|
+
"aria-hidden": true,
|
|
59
|
+
className: "flex justify-center gap-2",
|
|
60
|
+
children: Array.from({ length }, (_, index) => {
|
|
61
|
+
const current = focused && index === active;
|
|
62
|
+
return /* @__PURE__ */ jsx("div", {
|
|
63
|
+
"data-slot": index,
|
|
64
|
+
"data-digit": code[index],
|
|
65
|
+
className: cn("flex h-12 min-w-0 max-w-12 flex-1 items-center justify-center rounded-md border bg-background font-mono text-xl font-semibold text-foreground shadow-sm transition-colors before:content-[attr(data-digit)]", invalid ? "border-danger" : current ? "border-primary" : "border-border-strong", current && (invalid ? "ring-2 ring-danger/20" : "ring-2 ring-primary/20")),
|
|
66
|
+
children: code[index] === void 0 && (current ? /* @__PURE__ */ jsx("span", { className: "h-6 w-px animate-pulse bg-foreground" }) : /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-border-strong" }))
|
|
67
|
+
}, index);
|
|
68
|
+
})
|
|
69
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
70
|
+
ref: attach,
|
|
71
|
+
type: "text",
|
|
72
|
+
inputMode: "numeric",
|
|
73
|
+
autoComplete: "one-time-code",
|
|
74
|
+
pattern: `[0-9]{${length}}`,
|
|
75
|
+
spellCheck: false,
|
|
76
|
+
autoCorrect: "off",
|
|
77
|
+
autoCapitalize: "off",
|
|
78
|
+
"aria-invalid": invalid || void 0,
|
|
79
|
+
disabled,
|
|
80
|
+
value: code,
|
|
81
|
+
className: "absolute inset-0 size-full cursor-text bg-transparent text-transparent caret-transparent outline-none selection:bg-transparent disabled:cursor-not-allowed",
|
|
82
|
+
...rest,
|
|
83
|
+
onChange: (event) => {
|
|
84
|
+
const { data, dataTransfer } = event.nativeEvent;
|
|
85
|
+
commit(editedCode(event.target.value, data ?? dataTransfer?.getData("text/plain"), length));
|
|
86
|
+
},
|
|
87
|
+
onPaste: (event) => {
|
|
88
|
+
onPaste?.(event);
|
|
89
|
+
if (event.defaultPrevented) return;
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
const field = event.currentTarget;
|
|
92
|
+
const next = pastedCode(code, event.clipboardData.getData("text"), field.selectionStart ?? code.length, length);
|
|
93
|
+
commit(next);
|
|
94
|
+
setCaret(next.length);
|
|
95
|
+
},
|
|
96
|
+
onSelect: (event) => {
|
|
97
|
+
onSelect?.(event);
|
|
98
|
+
const field = event.currentTarget;
|
|
99
|
+
const start = field.selectionStart ?? code.length;
|
|
100
|
+
if (start === field.selectionEnd && start < field.value.length) field.setSelectionRange(start, start + 1);
|
|
101
|
+
setCaret(start);
|
|
102
|
+
},
|
|
103
|
+
onKeyDown: moveWithKeys,
|
|
104
|
+
onClick: placeAtPointer,
|
|
105
|
+
onFocus: (event) => {
|
|
106
|
+
setFocused(true);
|
|
107
|
+
onFocus?.(event);
|
|
108
|
+
},
|
|
109
|
+
onBlur: (event) => {
|
|
110
|
+
setFocused(false);
|
|
111
|
+
onBlur?.(event);
|
|
112
|
+
}
|
|
113
|
+
})]
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { OtpInput };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/primitives/otp-value.ts
|
|
2
|
+
/** The digits of `text`, at most `length`. */
|
|
3
|
+
const digitsOf = (text, length) => text.replace(/\D/g, "").slice(0, length);
|
|
4
|
+
/**
|
|
5
|
+
* The field's new code after an edit left `edited` in it. Text inserted in one go that holds a whole code, as a paste,
|
|
6
|
+
* the keyboard's suggestion or the system's autofill does, is the code, whatever was in the field before; anything else
|
|
7
|
+
* keeps the field's digits.
|
|
8
|
+
*/
|
|
9
|
+
const editedCode = (edited, inserted, length) => {
|
|
10
|
+
const whole = digitsOf(inserted ?? "", length);
|
|
11
|
+
return whole.length === length ? whole : digitsOf(edited, length);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The code after `pasted` went into `code` at box `start`: a whole code replaces the field's, and fewer digits fill the
|
|
15
|
+
* boxes from `start` on, over the digits they held.
|
|
16
|
+
*/
|
|
17
|
+
const pastedCode = (code, pasted, start, length) => {
|
|
18
|
+
const digits = pasted.replace(/\D/g, "");
|
|
19
|
+
if (digits.length >= length) return digits.slice(0, length);
|
|
20
|
+
return digitsOf(code.slice(0, start) + digits + code.slice(start + digits.length), length);
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { digitsOf, editedCode, pastedCode };
|
|
@@ -8,10 +8,12 @@ export type ResponsiveDialogProps = {
|
|
|
8
8
|
/** Buttons: right-aligned in the modal, stacked full width at the bottom of the sheet. */
|
|
9
9
|
actions: ReactNode;
|
|
10
10
|
children?: ReactNode;
|
|
11
|
+
/** Room for a row of cards or a table in the modal's body. */
|
|
12
|
+
wide?: boolean;
|
|
11
13
|
};
|
|
12
14
|
/**
|
|
13
15
|
* A modal centred on the screen from the `md` breakpoint up, and a sheet rising from the bottom on a phone, switching as
|
|
14
16
|
* the viewport does. Both close on Escape and on a press outside, and keep focus inside while open.
|
|
15
17
|
*/
|
|
16
|
-
export declare const ResponsiveDialog: ({ title, description, open, onClose, actions, children }: ResponsiveDialogProps) => import("react").JSX.Element | null;
|
|
18
|
+
export declare const ResponsiveDialog: ({ title, description, open, onClose, actions, children, wide }: ResponsiveDialogProps) => import("react").JSX.Element | null;
|
|
17
19
|
//#endregion
|
|
@@ -7,7 +7,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
* A modal centred on the screen from the `md` breakpoint up, and a sheet rising from the bottom on a phone, switching as
|
|
8
8
|
* the viewport does. Both close on Escape and on a press outside, and keep focus inside while open.
|
|
9
9
|
*/
|
|
10
|
-
const ResponsiveDialog = ({ title, description, open, onClose, actions, children }) => {
|
|
10
|
+
const ResponsiveDialog = ({ title, description, open, onClose, actions, children, wide }) => {
|
|
11
11
|
const desktop = useIsDesktop();
|
|
12
12
|
if (!open) return null;
|
|
13
13
|
if (desktop) return /* @__PURE__ */ jsx(Dialog, {
|
|
@@ -15,6 +15,7 @@ const ResponsiveDialog = ({ title, description, open, onClose, actions, children
|
|
|
15
15
|
description,
|
|
16
16
|
onClose,
|
|
17
17
|
actions,
|
|
18
|
+
wide,
|
|
18
19
|
children
|
|
19
20
|
});
|
|
20
21
|
return /* @__PURE__ */ jsx(Sheet, {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { AnchorHTMLAttributes } from "react";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { inApp } from "../app-shell/breadcrumb.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/route-link.tsx
|
|
4
|
+
/**
|
|
5
|
+
* An ordinary anchor to `to.href`: a plain click follows it in place with `to.onFollow`, while a middle click or one
|
|
6
|
+
* with Ctrl, Cmd, Shift or Alt opens it in a new tab or window, or saves it, as on any link. Every navigation a click
|
|
7
|
+
* starts is one of these, never a button that moves the page, so the browser shows where it goes and can open it.
|
|
8
|
+
*/
|
|
9
|
+
const RouteLink = ({ to, onClick, ...rest }) => /* @__PURE__ */ jsx("a", {
|
|
10
|
+
href: to.href,
|
|
11
|
+
onClick: (event) => {
|
|
12
|
+
onClick?.(event);
|
|
13
|
+
if (event.defaultPrevented || !inApp(event)) return;
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
to.onFollow();
|
|
16
|
+
},
|
|
17
|
+
...rest
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { RouteLink };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/primitives/textarea.tsx
|
|
5
|
+
const fit = (box) => {
|
|
6
|
+
box.style.height = "auto";
|
|
7
|
+
box.style.height = `${box.scrollHeight + box.offsetHeight - box.clientHeight}px`;
|
|
8
|
+
};
|
|
9
|
+
/** A text box of several lines that grows with its text instead of scrolling inside. */
|
|
10
|
+
const Textarea = ({ invalid, className, rows = 3, value, ...rest }) => {
|
|
11
|
+
const ref = useRef(null);
|
|
12
|
+
useLayoutEffect(() => {
|
|
13
|
+
if (ref.current) fit(ref.current);
|
|
14
|
+
}, [value]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const box = ref.current;
|
|
17
|
+
if (!box) return;
|
|
18
|
+
let width = box.clientWidth;
|
|
19
|
+
const observer = new ResizeObserver(() => {
|
|
20
|
+
if (box.clientWidth === width) return;
|
|
21
|
+
width = box.clientWidth;
|
|
22
|
+
fit(box);
|
|
23
|
+
});
|
|
24
|
+
observer.observe(box);
|
|
25
|
+
return () => observer.disconnect();
|
|
26
|
+
}, []);
|
|
27
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
28
|
+
ref,
|
|
29
|
+
rows,
|
|
30
|
+
value,
|
|
31
|
+
className: cn("block w-full resize-none overflow-hidden rounded-md border bg-background px-2.5 py-1.5 text-[13px] text-foreground shadow-sm outline-none placeholder:text-faint-foreground focus:border-primary focus:ring-2 focus:ring-primary/20", invalid ? "border-danger" : "border-border-strong", className),
|
|
32
|
+
...rest
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { Textarea };
|
|
@@ -3,60 +3,63 @@ import { Button } from "../primitives/button.js";
|
|
|
3
3
|
import { TriangleAlert } from "lucide-react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/record-view/field.tsx
|
|
6
|
-
const Field = ({ label, required, help, example, error, conflict, children }) => /* @__PURE__ */ jsxs("div", {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
className: "
|
|
11
|
-
children: [
|
|
12
|
-
className: "
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
const Field = ({ label, required, help, example, error, conflict, wide, children }) => /* @__PURE__ */ jsxs("div", {
|
|
7
|
+
className: wide ? "sm:col-span-2" : void 0,
|
|
8
|
+
children: [
|
|
9
|
+
/* @__PURE__ */ jsxs("div", {
|
|
10
|
+
className: "mb-1.5 flex items-center gap-2",
|
|
11
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
12
|
+
className: "text-[13px] font-medium text-foreground",
|
|
13
|
+
children: [label, required && /* @__PURE__ */ jsx("span", {
|
|
14
|
+
className: "ml-0.5 text-danger-text",
|
|
15
|
+
"aria-label": "required",
|
|
16
|
+
children: "*"
|
|
17
|
+
})]
|
|
18
|
+
}), conflict && /* @__PURE__ */ jsxs("span", {
|
|
19
|
+
className: "inline-flex items-center gap-1.5 text-warning-text",
|
|
20
|
+
title: `${conflict.user.name} saved a different value`,
|
|
21
|
+
children: [/* @__PURE__ */ jsx(TriangleAlert, { className: "size-3.5" }), /* @__PURE__ */ jsx(Avatar, {
|
|
22
|
+
initials: conflict.user.initials,
|
|
23
|
+
hue: conflict.user.hue,
|
|
24
|
+
size: "xs"
|
|
25
|
+
})]
|
|
15
26
|
})]
|
|
16
|
-
}),
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
}),
|
|
28
|
+
children,
|
|
29
|
+
help && /* @__PURE__ */ jsx("p", {
|
|
30
|
+
className: "mt-1.5 text-xs text-muted-foreground",
|
|
31
|
+
children: help
|
|
32
|
+
}),
|
|
33
|
+
example && /* @__PURE__ */ jsxs("p", {
|
|
34
|
+
className: "mt-0.5 text-xs text-faint-foreground",
|
|
35
|
+
children: ["Example: ", example]
|
|
36
|
+
}),
|
|
37
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
38
|
+
className: "mt-1.5 text-xs font-medium text-danger-text",
|
|
39
|
+
children: error
|
|
40
|
+
}),
|
|
41
|
+
conflict && /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: "mt-2 flex items-center justify-between gap-3 rounded-md border border-warning/30 bg-warning-soft px-3 py-2 text-xs text-warning-text",
|
|
43
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
44
|
+
/* @__PURE__ */ jsx("strong", { children: conflict.user.name }),
|
|
45
|
+
" saved this ",
|
|
46
|
+
conflict.ago,
|
|
47
|
+
": ",
|
|
48
|
+
/* @__PURE__ */ jsx("strong", { children: conflict.theirValue })
|
|
49
|
+
] }), /* @__PURE__ */ jsxs("span", {
|
|
50
|
+
className: "flex gap-1.5",
|
|
51
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
52
|
+
size: "sm",
|
|
53
|
+
onClick: conflict.onKeepMine,
|
|
54
|
+
children: "Keep mine"
|
|
55
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
56
|
+
size: "sm",
|
|
57
|
+
onClick: conflict.onUseTheirs,
|
|
58
|
+
children: "Use theirs"
|
|
59
|
+
})]
|
|
23
60
|
})]
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
help && /* @__PURE__ */ jsx("p", {
|
|
28
|
-
className: "mt-1.5 text-xs text-muted-foreground",
|
|
29
|
-
children: help
|
|
30
|
-
}),
|
|
31
|
-
example && /* @__PURE__ */ jsxs("p", {
|
|
32
|
-
className: "mt-0.5 text-xs text-faint-foreground",
|
|
33
|
-
children: ["Example: ", example]
|
|
34
|
-
}),
|
|
35
|
-
error && /* @__PURE__ */ jsx("p", {
|
|
36
|
-
className: "mt-1.5 text-xs font-medium text-danger-text",
|
|
37
|
-
children: error
|
|
38
|
-
}),
|
|
39
|
-
conflict && /* @__PURE__ */ jsxs("div", {
|
|
40
|
-
className: "mt-2 flex items-center justify-between gap-3 rounded-md border border-warning/30 bg-warning-soft px-3 py-2 text-xs text-warning-text",
|
|
41
|
-
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
42
|
-
/* @__PURE__ */ jsx("strong", { children: conflict.user.name }),
|
|
43
|
-
" saved this ",
|
|
44
|
-
conflict.ago,
|
|
45
|
-
": ",
|
|
46
|
-
/* @__PURE__ */ jsx("strong", { children: conflict.theirValue })
|
|
47
|
-
] }), /* @__PURE__ */ jsxs("span", {
|
|
48
|
-
className: "flex gap-1.5",
|
|
49
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
50
|
-
size: "sm",
|
|
51
|
-
onClick: conflict.onKeepMine,
|
|
52
|
-
children: "Keep mine"
|
|
53
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
54
|
-
size: "sm",
|
|
55
|
-
onClick: conflict.onUseTheirs,
|
|
56
|
-
children: "Use theirs"
|
|
57
|
-
})]
|
|
58
|
-
})]
|
|
59
|
-
})
|
|
60
|
-
] });
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
});
|
|
61
64
|
//#endregion
|
|
62
65
|
export { Field };
|