@protobase/ui 0.19.8 → 0.19.10

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 (85) hide show
  1. package/dist/app/app.js +4 -4
  2. package/dist/app/create/create-offered.js +9 -0
  3. package/dist/app/create/create-page.js +4 -4
  4. package/dist/app/create/create-record-form.js +26 -62
  5. package/dist/app/create/create-route.js +29 -0
  6. package/dist/app/create/form.d.ts +30 -0
  7. package/dist/app/create/form.js +72 -0
  8. package/dist/app/custom-section.js +1 -1
  9. package/dist/app/displays/map-display.js +1 -1
  10. package/dist/app/displays/record-card.js +18 -4
  11. package/dist/app/displays/record-link.js +1 -1
  12. package/dist/app/displays/use-map-records.js +2 -2
  13. package/dist/app/embedded-list/embedded-columns.js +2 -2
  14. package/dist/app/embedded-list/embedded-list.js +5 -5
  15. package/dist/app/embedded-list/use-embedded-rows.js +2 -2
  16. package/dist/app/field-editor.js +21 -5
  17. package/dist/app/gallery/gallery-section.js +1 -1
  18. package/dist/app/gallery/photo-details.js +2 -2
  19. package/dist/app/list-page.js +18 -17
  20. package/dist/app/pages/blocks/card-row.js +2 -2
  21. package/dist/app/pages/blocks/field.js +2 -2
  22. package/dist/app/pages/blocks/link.js +1 -1
  23. package/dist/app/pages/blocks/modal-form.js +6 -6
  24. package/dist/app/pages/blocks/progress.js +1 -1
  25. package/dist/app/pages/blocks/record-card.js +2 -2
  26. package/dist/app/pages/blocks/record-row.js +1 -1
  27. package/dist/app/pages/blocks/show.js +2 -2
  28. package/dist/app/pages/blocks/stat.js +3 -3
  29. package/dist/app/pages/blocks/table.js +1 -1
  30. package/dist/app/pages/field-value.js +5 -2
  31. package/dist/app/pages/record-actions.js +2 -2
  32. package/dist/app/pages/use-run-action.js +2 -2
  33. package/dist/app/record-page.js +15 -11
  34. package/dist/app/record-row/record-row-body.js +2 -2
  35. package/dist/app/record-row/saved-record-row.js +1 -1
  36. package/dist/app/related-section.js +1 -1
  37. package/dist/app/searches/saved-search-rows.js +1 -1
  38. package/dist/app/searches/saved-search-section.js +2 -2
  39. package/dist/app/searches/saved-searches.js +2 -2
  40. package/dist/app/searches/search-controls.js +1 -1
  41. package/dist/app/searches/search-name-dialog.js +1 -1
  42. package/dist/app/searches/update-search-dialog.js +2 -2
  43. package/dist/app/searches/use-filter-chips.js +1 -1
  44. package/dist/app/searches/use-save-search.js +2 -2
  45. package/dist/app/searches/use-saved-searches.js +1 -1
  46. package/dist/app/searches/use-search-row.js +1 -1
  47. package/dist/app/sensitive-field.js +1 -1
  48. package/dist/app/use-app-address.js +1 -1
  49. package/dist/app/value-badge.js +1 -1
  50. package/dist/auth/sign-in-prompt.js +1 -1
  51. package/dist/data-grid/cells.js +1 -1
  52. package/dist/index.d.ts +9 -1
  53. package/dist/index.js +10 -2
  54. package/dist/live/columns-from-model.js +3 -0
  55. package/dist/live/grid-loader.js +1 -1
  56. package/dist/live/use-filter-config.js +1 -1
  57. package/dist/markdown/markdown-content.js +18 -0
  58. package/dist/markdown/markdown-editor.js +46 -0
  59. package/dist/markdown/markdown-excerpt.js +36 -0
  60. package/dist/markdown/markdown-hint.js +5 -0
  61. package/dist/markdown/markdown-html.js +59 -0
  62. package/dist/markdown/markdown.js +15 -0
  63. package/dist/package.js +1 -1
  64. package/dist/primitives/choices.d.ts +22 -0
  65. package/dist/primitives/choices.js +33 -0
  66. package/dist/primitives/dialog.d.ts +15 -0
  67. package/dist/primitives/menu-select.d.ts +14 -0
  68. package/dist/primitives/segmented.d.ts +23 -0
  69. package/dist/primitives/textarea.d.ts +9 -0
  70. package/dist/record-view/field.d.ts +27 -0
  71. package/dist/record-view/section.d.ts +10 -0
  72. package/dist/shell/assets/app-GKl91FLS.js +69 -0
  73. package/dist/shell/assets/{index-cMmFPuUQ.js → index-BB2BN-6s.js} +1 -1
  74. package/dist/shell/assets/index-DxV5Rp_F.css +2 -0
  75. package/dist/shell/assets/{maplibre-9XYrB9k6.js → maplibre-DHotMteL.js} +2 -2
  76. package/dist/shell/assets/markdown-content-C4zE1qZp.js +67 -0
  77. package/dist/shell/assets/{record-map-OBtKCiUB.js → record-map-BBbVY6vn.js} +1 -1
  78. package/dist/shell/assets/{records-map-C4oMPL5C.js → records-map-ur6HR-I5.js} +1 -1
  79. package/dist/shell/assets/shared-protobase-ui-D7kkYiTL.js +1 -0
  80. package/dist/shell/index.html +4 -4
  81. package/dist/shell/protobase.shell.json +1 -1
  82. package/package.json +6 -4
  83. package/dist/shell/assets/app-D8i5r_6U.js +0 -68
  84. package/dist/shell/assets/index-Cuulz26c.css +0 -2
  85. package/dist/shell/assets/shared-protobase-ui-CN1HmyDa.js +0 -1
@@ -0,0 +1,36 @@
1
+ //#region src/markdown/markdown-excerpt.ts
2
+ const blockMarks = [
3
+ [/^\s*(```|~~~).*$/, ""],
4
+ [/^\s*\[[^\]]+\]:\s+\S.*$/, ""],
5
+ [/^\s*([-*_]\s*){3,}$/, ""],
6
+ [/^\s*\|?(\s*:?-+:?\s*\|)+\s*:?-*:?\s*$/, ""],
7
+ [/^\s*#{1,6}\s+/, ""],
8
+ [/^\s*(>\s?)+/, ""],
9
+ [/^\s*([-*+]|\d+[.)])\s+(\[[ xX]\]\s+)?/, ""]
10
+ ];
11
+ const inlineMarks = [
12
+ [/!\[([^\]]*)\]\([^)]*\)/g, "$1"],
13
+ [/\[([^\]]*)\]\([^)]*\)/g, "$1"],
14
+ [/\[([^\]]+)\]\[[^\]]*\]/g, "$1"],
15
+ [/<((?:https?|mailto):[^>\s]+)>/gi, "$1"],
16
+ [/<\/?[a-z][^>]*>/gi, ""],
17
+ [/`([^`]+)`/g, "$1"],
18
+ [/(?<!\\)(\*\*|__)(?=\S)(.+?)(?<=\S)\1/g, "$2"],
19
+ [/(?<![\\\w*])\*(?=\S)(.+?)(?<=\S)\*/g, "$1"],
20
+ [/(?<![\\\w])_(?=\S)(.+?)(?<=\S)_(?!\w)/g, "$1"],
21
+ [/(?<!\\)~~(.+?)~~/g, "$1"],
22
+ [/\s*\|\s*/g, " "],
23
+ [/\\([\\`*_{}[\]()#+\-.!>~|])/g, "$1"]
24
+ ];
25
+ const longest = 200;
26
+ /**
27
+ * Markdown as one line of plain text, as a list's cell shows it: the text of its headings, lists, links and emphasis
28
+ * without their marks, at most 200 characters.
29
+ */
30
+ const markdownExcerpt = (source) => {
31
+ const lines = source.split(/\r?\n/).map((line) => blockMarks.reduce((text, [mark, kept]) => text.replace(mark, kept), line));
32
+ const text = inlineMarks.reduce((joined, [mark, kept]) => joined.replace(mark, kept), lines.join("\n")).replace(/\s+/g, " ").trim();
33
+ return text.length > longest ? `${text.slice(0, 199).trimEnd()}…` : text;
34
+ };
35
+ //#endregion
36
+ export { markdownExcerpt };
@@ -0,0 +1,5 @@
1
+ //#region src/markdown/markdown-hint.ts
2
+ /** Whether the view shows a field as markdown (`.markdown()`), which only a text field takes. */
3
+ const isMarkdown = (field, hints) => field.type === "text" && Boolean(hints?.markdown);
4
+ //#endregion
5
+ export { isMarkdown };
@@ -0,0 +1,59 @@
1
+ import { Marked } from "marked";
2
+ //#region src/markdown/markdown-html.ts
3
+ const escapes = {
4
+ "&": "&amp;",
5
+ "<": "&lt;",
6
+ ">": "&gt;",
7
+ "\"": "&quot;",
8
+ "'": "&#39;"
9
+ };
10
+ const escapeHtml = (text) => text.replace(/[&<>"']/g, (char) => escapes[char]);
11
+ const scheme = /^([a-z][a-z0-9+.-]*):/i;
12
+ const linkSchemes = /* @__PURE__ */ new Set([
13
+ "http",
14
+ "https",
15
+ "mailto",
16
+ "tel"
17
+ ]);
18
+ const imageSchemes = /* @__PURE__ */ new Set(["http", "https"]);
19
+ const ignoredByBrowsers = /[\u0000-\u0020\u007f]/g;
20
+ const safeAddress = (href, schemes) => {
21
+ const named = scheme.exec(href.replace(ignoredByBrowsers, ""))?.[1]?.toLowerCase();
22
+ if (named !== void 0 && !schemes.has(named)) return null;
23
+ try {
24
+ return encodeURI(href).replace(/%25/g, "%");
25
+ } catch (error) {
26
+ if (error instanceof URIError) return null;
27
+ throw error;
28
+ }
29
+ };
30
+ const titled = (title) => title ? ` title="${escapeHtml(title)}"` : "";
31
+ const parser = ({ inLink = false }) => new Marked({
32
+ gfm: true,
33
+ breaks: true,
34
+ renderer: {
35
+ html: ({ text, block }) => block ? `<p>${escapeHtml(text)}</p>` : escapeHtml(text),
36
+ link({ href, title, tokens }) {
37
+ const text = this.parser.parseInline(tokens);
38
+ const address = inLink ? null : safeAddress(href, linkSchemes);
39
+ if (address === null) return text;
40
+ return `<a href="${escapeHtml(address)}"${titled(title)} target="_blank" rel="noopener noreferrer nofollow">${text}</a>`;
41
+ },
42
+ image({ href, title, text }) {
43
+ const address = inLink ? null : safeAddress(href, imageSchemes);
44
+ if (address === null) return escapeHtml(text);
45
+ return `<img src="${escapeHtml(address)}" alt="${escapeHtml(text)}"${titled(title)} loading="lazy">`;
46
+ }
47
+ }
48
+ });
49
+ const parsers = {
50
+ inLink: parser({ inLink: true }),
51
+ page: parser({})
52
+ };
53
+ /**
54
+ * Markdown as HTML (GitHub's flavour, line breaks kept): raw HTML escaped, links and images only to http(s), mailto and
55
+ * tel addresses or relative ones, and links opening in a new tab. The page sanitises the result again before showing it.
56
+ */
57
+ const markdownHtml = (source, options = {}) => (options.inLink ? parsers.inLink : parsers.page).parse(source, { async: false });
58
+ //#endregion
59
+ export { markdownHtml };
@@ -0,0 +1,15 @@
1
+ import { cn } from "../lib/cn.js";
2
+ import { Suspense, lazy } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/markdown/markdown.tsx
5
+ const MarkdownContent = lazy(() => import("./markdown-content.js").then((module) => ({ default: module.MarkdownContent })));
6
+ /** Markdown rendered and sanitised; its source, line breaks kept, until the renderer has loaded. */
7
+ const Markdown = (props) => /* @__PURE__ */ jsx(Suspense, {
8
+ fallback: /* @__PURE__ */ jsx("div", {
9
+ className: cn("whitespace-pre-line break-words", props.className),
10
+ children: props.source
11
+ }),
12
+ children: /* @__PURE__ */ jsx(MarkdownContent, { ...props })
13
+ });
14
+ //#endregion
15
+ export { Markdown };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.19.8";
2
+ var version = "0.19.10";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/primitives/choices.d.ts
3
+ export type ChoiceOption<T extends string> = {
4
+ value: T;
5
+ label: string;
6
+ description?: ReactNode;
7
+ disabled?: boolean;
8
+ };
9
+ /**
10
+ * `name` groups the radio buttons, so that the arrow keys move between them; `label` names the group for screen
11
+ * readers. A disabled option shows and cannot be chosen.
12
+ */
13
+ export type ChoicesProps<T extends string> = {
14
+ name: string;
15
+ label?: string;
16
+ options: Array<ChoiceOption<T>>;
17
+ value: T | undefined;
18
+ onChange: (value: T) => void;
19
+ };
20
+ /** One of a few options, each a card of its own row with a line that says what it means; the chosen one is outlined. */
21
+ export declare const Choices: <T extends string>({ name, label, options, value, onChange }: ChoicesProps<T>) => import("react").JSX.Element;
22
+ //#endregion
@@ -0,0 +1,33 @@
1
+ import { cn } from "../lib/cn.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/primitives/choices.tsx
4
+ /** One of a few options, each a card of its own row with a line that says what it means; the chosen one is outlined. */
5
+ const Choices = ({ name, label, options, value, onChange }) => /* @__PURE__ */ jsx("div", {
6
+ role: "radiogroup",
7
+ "aria-label": label,
8
+ className: "flex flex-col gap-2",
9
+ children: options.map((option) => /* @__PURE__ */ jsxs("label", {
10
+ className: cn("block rounded-lg border p-3 transition-colors has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-primary", option.disabled ? "cursor-default bg-background opacity-60" : option.value === value ? "cursor-pointer border-primary bg-primary-soft" : "cursor-pointer bg-background hover:bg-surface"),
11
+ children: [
12
+ /* @__PURE__ */ jsx("input", {
13
+ type: "radio",
14
+ name,
15
+ value: option.value,
16
+ checked: option.value === value,
17
+ disabled: option.disabled,
18
+ onChange: () => onChange(option.value),
19
+ className: "sr-only"
20
+ }),
21
+ /* @__PURE__ */ jsx("span", {
22
+ className: cn("block font-medium", option.value === value && "text-primary-text"),
23
+ children: option.label
24
+ }),
25
+ option.description && /* @__PURE__ */ jsx("span", {
26
+ className: "mt-0.5 block text-xs leading-snug text-muted-foreground",
27
+ children: option.description
28
+ })
29
+ ]
30
+ }, option.value))
31
+ });
32
+ //#endregion
33
+ export { Choices };
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/primitives/dialog.d.ts
3
+ export type DialogProps = {
4
+ title: string;
5
+ description?: ReactNode;
6
+ onClose: () => void;
7
+ /** Buttons, rendered right-aligned. */
8
+ actions: ReactNode;
9
+ children?: ReactNode;
10
+ /** Room for a row of cards or a table in the body, instead of a short question. */
11
+ wide?: boolean;
12
+ };
13
+ /** A modal with a title, optional body and action buttons. Closes on Escape and on a click outside. */
14
+ export declare const Dialog: ({ title, description, onClose, actions, children, wide }: DialogProps) => import("react").JSX.Element;
15
+ //#endregion
@@ -0,0 +1,14 @@
1
+ import { SegmentedOption } from "./segmented.js";
2
+ //#region src/primitives/menu-select.d.ts
3
+ export type MenuSelectProps<T extends string> = {
4
+ value: T;
5
+ options: Array<SegmentedOption<T>>;
6
+ onChange: (value: T) => void;
7
+ label: string;
8
+ };
9
+ /**
10
+ * The choices of a Segmented switch in a menu, for a row without room for every option: a button with the chosen
11
+ * option's icon and label, opening the options under its right edge.
12
+ */
13
+ export declare const MenuSelect: <T extends string>({ value, options, onChange, label }: MenuSelectProps<T>) => import("react").JSX.Element;
14
+ //#endregion
@@ -0,0 +1,23 @@
1
+ import { ComponentType } from "react";
2
+ //#region src/primitives/segmented.d.ts
3
+ export type SegmentedOption<T extends string> = {
4
+ value: T;
5
+ label: string;
6
+ icon?: ComponentType<{
7
+ className?: string;
8
+ }>;
9
+ };
10
+ /**
11
+ * `md` matches a medium Button, for a switch that sits among buttons. `iconOnly` draws each option as its icon alone,
12
+ * its label the button's name, for a row without room for the words.
13
+ */
14
+ export type SegmentedProps<T extends string> = {
15
+ value: T;
16
+ options: Array<SegmentedOption<T>>;
17
+ onChange: (value: T) => void;
18
+ label?: string;
19
+ size?: 'sm' | 'md';
20
+ iconOnly?: boolean;
21
+ };
22
+ export declare const Segmented: <T extends string>({ value, options, onChange, label, size, iconOnly }: SegmentedProps<T>) => import("react").JSX.Element;
23
+ //#endregion
@@ -0,0 +1,9 @@
1
+ import { TextareaHTMLAttributes } from "react";
2
+ //#region src/primitives/textarea.d.ts
3
+ export type TextareaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value'> & {
4
+ value: string;
5
+ invalid?: boolean;
6
+ };
7
+ /** A text box of several lines that grows with its text instead of scrolling inside. */
8
+ export declare const Textarea: ({ invalid, className, rows, value, ...rest }: TextareaProps) => import("react").JSX.Element;
9
+ //#endregion
@@ -0,0 +1,27 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/record-view/field.d.ts
3
+ export type FieldConflict = {
4
+ user: {
5
+ name: string;
6
+ initials: string;
7
+ hue: number;
8
+ };
9
+ ago: string;
10
+ theirValue: string;
11
+ onKeepMine: () => void;
12
+ onUseTheirs: () => void;
13
+ };
14
+ export type FieldProps = {
15
+ label: string;
16
+ /** Marks the label with an asterisk. */
17
+ required?: boolean;
18
+ help?: string;
19
+ example?: string;
20
+ error?: string;
21
+ conflict?: FieldConflict;
22
+ /** Takes the whole row of a grid of two columns, as a text of several lines does. */
23
+ wide?: boolean;
24
+ children: ReactNode;
25
+ };
26
+ export declare const Field: ({ label, required, help, example, error, conflict, wide, children }: FieldProps) => import("react").JSX.Element;
27
+ //#endregion
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/record-view/section.d.ts
3
+ /** A titled part of the record page; `actions` sit at the end of the title's line. */
4
+ export declare const Section: ({ title, help, actions, children }: {
5
+ title: string;
6
+ help?: string;
7
+ actions?: ReactNode;
8
+ children: ReactNode;
9
+ }) => import("react").JSX.Element;
10
+ //#endregion