@mdbase-dev/ui 0.1.0-beta.126 → 0.1.0-beta.147

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 (72) hide show
  1. package/README.md +159 -0
  2. package/css/collections.css +978 -0
  3. package/css/controls.css +26 -1
  4. package/css/screens.css +44 -2
  5. package/dist/AppSwitcher.js +187 -33
  6. package/dist/Checkbox.d.ts +14 -0
  7. package/dist/Checkbox.js +32 -0
  8. package/dist/CommandPalette.d.ts +4 -2
  9. package/dist/CommandPalette.js +275 -103
  10. package/dist/Dialog.js +72 -25
  11. package/dist/Feedback.js +763 -229
  12. package/dist/FeedbackMarkup.d.ts +1 -1
  13. package/dist/FeedbackMarkup.js +326 -125
  14. package/dist/FeedbackVerification.js +115 -68
  15. package/dist/SaveNotice.js +71 -28
  16. package/dist/Select.js +183 -135
  17. package/dist/SelectList.js +59 -14
  18. package/dist/ThemeSelect.js +55 -4
  19. package/dist/apps.d.ts +1 -0
  20. package/dist/apps.js +50 -29
  21. package/dist/brand.js +251 -68
  22. package/dist/codemirror.js +116 -87
  23. package/dist/collections/CollectionGate.d.ts +52 -0
  24. package/dist/collections/CollectionGate.js +456 -0
  25. package/dist/collections/CollectionSaveWarning.d.ts +10 -0
  26. package/dist/collections/CollectionSaveWarning.js +28 -0
  27. package/dist/collections/CollectionSwitcher.d.ts +26 -0
  28. package/dist/collections/CollectionSwitcher.js +579 -0
  29. package/dist/collections/SetupReview.d.ts +9 -0
  30. package/dist/collections/SetupReview.js +175 -0
  31. package/dist/collections/copy.d.ts +100 -0
  32. package/dist/collections/copy.js +937 -0
  33. package/dist/collections/dialogs.d.ts +27 -0
  34. package/dist/collections/dialogs.js +169 -0
  35. package/dist/collections/model.d.ts +146 -0
  36. package/dist/collections/model.js +337 -0
  37. package/dist/collections/mount.d.ts +18 -0
  38. package/dist/collections/mount.js +56 -0
  39. package/dist/collections/parts.d.ts +86 -0
  40. package/dist/collections/parts.js +439 -0
  41. package/dist/collections/testing.d.ts +19 -0
  42. package/dist/collections/testing.js +39 -0
  43. package/dist/collections/use-collection-ui.d.ts +140 -0
  44. package/dist/collections/use-collection-ui.js +236 -0
  45. package/dist/collections.d.ts +19 -0
  46. package/dist/collections.js +13 -0
  47. package/dist/feedback-data.d.ts +13 -12
  48. package/dist/feedback-data.js +365 -168
  49. package/dist/flock.d.ts +63 -0
  50. package/dist/flock.js +140 -0
  51. package/dist/i18n.d.ts +6 -0
  52. package/dist/i18n.js +71 -0
  53. package/dist/locales/de/messages.json +1 -0
  54. package/dist/locales/en/messages.json +1 -0
  55. package/dist/locales/es/messages.json +1 -0
  56. package/dist/locales/fr/messages.json +1 -0
  57. package/dist/locales/it/messages.json +1 -0
  58. package/dist/locales/ja/messages.json +1 -0
  59. package/dist/locales/ko/messages.json +1 -0
  60. package/dist/locales/pt/messages.json +1 -0
  61. package/dist/locales/ru/messages.json +1 -0
  62. package/dist/locales/zh/messages.json +1 -0
  63. package/dist/mark-activity.js +82 -75
  64. package/dist/permission-selection.js +27 -17
  65. package/dist/popover.js +138 -130
  66. package/dist/screens.d.ts +20 -3
  67. package/dist/screens.js +275 -6
  68. package/dist/select-model.js +34 -28
  69. package/dist/text-diff.d.ts +1 -0
  70. package/dist/text-diff.js +106 -74
  71. package/dist/theme.js +34 -40
  72. package/package.json +52 -7
package/css/controls.css CHANGED
@@ -598,6 +598,31 @@ textarea.mdbase-field {
598
598
  outline-offset: 2px;
599
599
  }
600
600
 
601
+ /* Checkbox row: the label is the 44px target; the box keeps its size. */
602
+ .mdbase-check {
603
+ display: flex;
604
+ align-items: center;
605
+ gap: var(--space-2);
606
+ min-height: 44px;
607
+ cursor: pointer;
608
+ }
609
+
610
+ .mdbase-check:has(.mdbase-checkbox:disabled) {
611
+ cursor: not-allowed;
612
+ }
613
+
614
+ .mdbase-check-group {
615
+ min-width: 0;
616
+ margin: 0;
617
+ padding: 0;
618
+ border: 0;
619
+ }
620
+
621
+ .mdbase-check-group > legend {
622
+ margin-bottom: var(--space-2);
623
+ padding: 0;
624
+ }
625
+
601
626
  @media (pointer: coarse) {
602
627
  /* Expand the hit target, not the visual control or its containing row. */
603
628
  .mdbase-checkbox::before,
@@ -677,7 +702,7 @@ textarea.mdbase-field {
677
702
  }
678
703
 
679
704
  @keyframes mdbase-popover-in {
680
- from { opacity: 0; transform: translateY(-3px); }
705
+ from { transform: translateY(-3px); }
681
706
  }
682
707
 
683
708
  /* Save notices: small, quiet, and gone once there is nothing to act on. */
package/css/screens.css CHANGED
@@ -17,6 +17,12 @@
17
17
  text-align: center;
18
18
  }
19
19
 
20
+ .mdbase-opening .wordmark-mark > .mdbase-mark {
21
+ display: block;
22
+ width: 100%;
23
+ height: 100%;
24
+ }
25
+
20
26
  .mdbase-opening-message p,
21
27
  .mdbase-opening-message small {
22
28
  display: block;
@@ -38,18 +44,54 @@
38
44
  color: var(--color-danger);
39
45
  }
40
46
 
47
+ /* The launch screen: the mark the flock flies onto, then the quiet line. */
48
+ .mdbase-opening-launch-mark {
49
+ display: block;
50
+ width: 42px;
51
+ height: 42px;
52
+ margin-bottom: var(--space-2);
53
+ }
54
+
55
+ .mdbase-opening-launch-mark .mdbase-mark {
56
+ display: block;
57
+ width: 100%;
58
+ height: 100%;
59
+ }
60
+
61
+ /* While the flock flies, the canvas draws the mark; the real one takes over when it lands. */
62
+ .mdbase-opening-launch[data-entrance="flying"] .mdbase-opening-launch-mark {
63
+ visibility: hidden;
64
+ }
65
+
66
+ .mdbase-opening-launch-text {
67
+ transition: opacity var(--motion-slow) var(--ease-out);
68
+ }
69
+
70
+ .mdbase-opening-launch[data-entrance="flying"] .mdbase-opening-launch-text {
71
+ opacity: 0;
72
+ }
73
+
74
+ .mdbase-opening-flock {
75
+ position: fixed;
76
+ inset: 0;
77
+ width: 100vw;
78
+ height: 100vh;
79
+ pointer-events: none;
80
+ }
81
+
41
82
  /* Before a collection is open. */
42
83
  .mdbase-connect-screen {
43
84
  min-height: 100%;
44
85
  display: grid;
45
86
  place-items: center;
46
- padding: 32px;
87
+ /* The extra bottom padding lifts short content slightly above centre; tall
88
+ * content just grows downward, so the top never clips (no negative margin). */
89
+ padding: 32px 32px calc(32px + 8vh);
47
90
  background: var(--color-canvas);
48
91
  }
49
92
 
50
93
  .mdbase-connect-screen section {
51
94
  width: min(100%, 520px);
52
- margin-top: -4vh;
53
95
  }
54
96
 
55
97
  .mdbase-connect-screen h1 {
@@ -1,4 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { i18n as _i18n } from "@lingui/core";
3
+ import { useLocaleRevision } from "./i18n.js";
2
4
  import { useId, useRef, useState } from "react";
3
5
  import { mdbaseAppHref, withAppUrls } from "./apps.js";
4
6
  import { MdbaseAppMark, Wordmark } from "./brand.js";
@@ -13,39 +15,191 @@ let entrancePlayed = false;
13
15
  * Its mark shows the page's activity (see `mark-activity`): progress, then a held loop,
14
16
  * then `motion`. Without a `motion` it plays `keys-first` the first time it appears.
15
17
  */
16
- export function AppSwitcher({ current, urls = {}, motion }) {
17
- const menuId = useId();
18
- const triggerRef = useRef(null);
19
- const [open, setOpen] = useState(false);
20
- const [entrance] = useState(() => {
21
- if (motion || entrancePlayed || typeof window === "undefined")
22
- return undefined;
23
- entrancePlayed = true;
24
- return "keys-first";
25
- });
26
- const activity = useMdbaseMarkActivity();
27
- const close = (refocus) => {
28
- setOpen(false);
29
- if (refocus)
30
- triggerRef.current?.focus();
31
- };
32
- return _jsxs(_Fragment, { children: [_jsxs("button", { ref: triggerRef, type: "button", className: "mdbase-app-switcher", "aria-label": `mdbase ${current}: open this collection in another app`, "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? menuId : undefined, title: "Open in another mdbase app", onClick: () => (open ? close(false) : setOpen(true)), onKeyDown: (event) => {
33
- if (!open && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
34
- event.preventDefault();
35
- setOpen(true);
36
- }
37
- }, children: [_jsx(Wordmark, { app: current, motion: activity.busy ?? motion ?? entrance, signal: activity.signal, progress: activity.progress }), _jsx("svg", { className: "mdbase-app-switcher-chevron", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "m7 10 5 5 5-5" }) })] }), open ? _jsx(AppMenu, { id: menuId, apps: withAppUrls(urls), current: current, triggerRef: triggerRef, onClose: close }) : null] });
18
+ export function AppSwitcher({
19
+ current,
20
+ urls = {},
21
+ motion
22
+ }) {
23
+ useLocaleRevision();
24
+ const menuId = useId();
25
+ const triggerRef = useRef(null);
26
+ const [open, setOpen] = useState(false);
27
+ const [entrance] = useState(() => {
28
+ if (motion || entrancePlayed || typeof window === "undefined") return undefined;
29
+ entrancePlayed = true;
30
+ return "keys-first";
31
+ });
32
+ const activity = useMdbaseMarkActivity();
33
+ const close = refocus => {
34
+ setOpen(false);
35
+ if (refocus) triggerRef.current?.focus();
36
+ };
37
+ return _jsxs(_Fragment, {
38
+ children: [_jsxs("button", {
39
+ ref: triggerRef,
40
+ type: "button",
41
+ className: "mdbase-app-switcher",
42
+ "aria-label": _i18n._(
43
+ /** i18n */
44
+ {
45
+ id: "54P3Y1",
46
+ message: "mdbase {current}: open this collection in another app",
47
+ values: {
48
+ current: current
49
+ }
50
+ }),
51
+ "aria-haspopup": "menu",
52
+ "aria-expanded": open,
53
+ "aria-controls": open ? menuId : undefined,
54
+ title: _i18n._(
55
+ /** i18n */
56
+ {
57
+ id: "38fGkV",
58
+ message: "Open in another mdbase app"
59
+ }),
60
+ onClick: () => open ? close(false) : setOpen(true),
61
+ onKeyDown: event => {
62
+ if (!open && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
63
+ event.preventDefault();
64
+ setOpen(true);
65
+ }
66
+ },
67
+ children: [_jsx(Wordmark, {
68
+ app: current,
69
+ motion: activity.busy ?? motion ?? entrance,
70
+ signal: activity.signal,
71
+ progress: activity.progress
72
+ }), _jsx("svg", {
73
+ className: "mdbase-app-switcher-chevron",
74
+ viewBox: "0 0 24 24",
75
+ "aria-hidden": "true",
76
+ children: _jsx("path", {
77
+ d: "m7 10 5 5 5-5"
78
+ })
79
+ })]
80
+ }), open ? _jsx(AppMenu, {
81
+ id: menuId,
82
+ apps: withAppUrls(urls),
83
+ current: current,
84
+ triggerRef: triggerRef,
85
+ onClose: close
86
+ }) : null]
87
+ });
38
88
  }
39
- function AppMenu({ id, apps, current, triggerRef, onClose }) {
40
- const menuRef = useRef(null);
41
- useMenuPopover(menuRef, triggerRef, onClose, { width: 320, focus: 'a[role="menuitem"]' });
42
- const heading = new URL(location.href).searchParams.has("collection") ? "Open this collection in" : "mdbase apps";
43
- return _jsxs("div", { ref: menuRef, id: id, className: "mdbase-menu", popover: "manual", role: "menu", "aria-label": heading, tabIndex: -1, onKeyDown: (event) => moveMenuFocus(event, menuRef.current), children: [_jsx("div", { className: "mdbase-menu-heading", role: "presentation", children: heading }), _jsx("div", { className: "mdbase-menu-list", children: apps.map((app) => _jsx(AppItem, { app: app, current: app.id === current, onOpen: () => onClose(false) }, app.id)) }), _jsx("p", { className: "mdbase-menu-note", children: "Opens in a new tab" })] });
89
+ function AppMenu({
90
+ id,
91
+ apps,
92
+ current,
93
+ triggerRef,
94
+ onClose
95
+ }) {
96
+ useLocaleRevision();
97
+ const menuRef = useRef(null);
98
+ useMenuPopover(menuRef, triggerRef, onClose, {
99
+ width: 320,
100
+ focus: 'a[role="menuitem"]'
101
+ });
102
+ const heading = new URL(location.href).searchParams.has("collection") ? _i18n._(
103
+ /** i18n */
104
+ {
105
+ id: "xxbaLR",
106
+ message: "Open this collection in"
107
+ }) : _i18n._(
108
+ /** i18n */
109
+ {
110
+ id: "vDwCOr",
111
+ message: "mdbase apps"
112
+ });
113
+ return _jsxs("div", {
114
+ ref: menuRef,
115
+ id: id,
116
+ className: "mdbase-menu",
117
+ popover: "manual",
118
+ role: "menu",
119
+ "aria-label": heading,
120
+ tabIndex: -1,
121
+ onKeyDown: event => moveMenuFocus(event, menuRef.current),
122
+ children: [_jsx("div", {
123
+ className: "mdbase-menu-heading",
124
+ role: "presentation",
125
+ children: heading
126
+ }), _jsx("div", {
127
+ className: "mdbase-menu-list",
128
+ children: apps.map(app => _jsx(AppItem, {
129
+ app: app,
130
+ current: app.id === current,
131
+ onOpen: () => onClose(false)
132
+ }, app.id))
133
+ }), _jsx("p", {
134
+ className: "mdbase-menu-note",
135
+ children: _i18n._(
136
+ /** i18n */
137
+ {
138
+ id: "XDzAnp",
139
+ message: "Opens in a new tab"
140
+ })
141
+ })]
142
+ });
44
143
  }
45
- function AppItem({ app, current, onOpen }) {
46
- const copy = _jsxs(_Fragment, { children: [_jsx(MdbaseAppMark, { app: app.id, className: "mdbase-app-menu-mark" }), _jsxs("span", { className: "mdbase-menu-copy", children: [_jsx("strong", { children: app.name }), _jsx("small", { children: app.description })] })] });
47
- if (current) {
48
- return _jsxs("div", { className: "mdbase-menu-item is-current", role: "menuitem", "aria-current": "page", "aria-disabled": "true", tabIndex: -1, children: [copy, _jsx("span", { className: "mdbase-app-menu-current", children: "Current" })] });
49
- }
50
- return _jsxs("a", { className: "mdbase-menu-item", role: "menuitem", href: mdbaseAppHref(app.url, location.href), target: "_blank", rel: "noopener", onClick: onOpen, children: [copy, _jsx("svg", { className: "mdbase-app-menu-external", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M10 5.5H5.5v13h13V14M13.5 5.5h5v5M18.5 5.5 11 13" }) }), _jsx("span", { className: "mdbase-visually-hidden", children: " (opens in a new tab)" })] });
144
+ function AppItem({
145
+ app,
146
+ current,
147
+ onOpen
148
+ }) {
149
+ useLocaleRevision();
150
+ const copy = _jsxs(_Fragment, {
151
+ children: [_jsx(MdbaseAppMark, {
152
+ app: app.id,
153
+ className: "mdbase-app-menu-mark"
154
+ }), _jsxs("span", {
155
+ className: "mdbase-menu-copy",
156
+ children: [_jsx("strong", {
157
+ children: app.name
158
+ }), _jsx("small", {
159
+ children: app.description
160
+ })]
161
+ })]
162
+ });
163
+ if (current) {
164
+ return _jsxs("div", {
165
+ className: "mdbase-menu-item is-current",
166
+ role: "menuitem",
167
+ "aria-current": "page",
168
+ "aria-disabled": "true",
169
+ tabIndex: -1,
170
+ children: [copy, _jsx("span", {
171
+ className: "mdbase-app-menu-current",
172
+ children: _i18n._(
173
+ /** i18n */
174
+ {
175
+ id: "Hp1l6f",
176
+ message: "Current"
177
+ })
178
+ })]
179
+ });
180
+ }
181
+ return _jsxs("a", {
182
+ className: "mdbase-menu-item",
183
+ role: "menuitem",
184
+ href: mdbaseAppHref(app.url, location.href),
185
+ target: "_blank",
186
+ rel: "noopener",
187
+ onClick: onOpen,
188
+ children: [copy, _jsx("svg", {
189
+ className: "mdbase-app-menu-external",
190
+ viewBox: "0 0 24 24",
191
+ "aria-hidden": "true",
192
+ children: _jsx("path", {
193
+ d: "M10 5.5H5.5v13h13V14M13.5 5.5h5v5M18.5 5.5 11 13"
194
+ })
195
+ }), _jsxs("span", {
196
+ className: "mdbase-visually-hidden",
197
+ children: [" ", _i18n._(
198
+ /** i18n */
199
+ {
200
+ id: "75iTY6",
201
+ message: "(opens in a new tab)"
202
+ })]
203
+ })]
204
+ });
51
205
  }
@@ -0,0 +1,14 @@
1
+ import type { InputHTMLAttributes, JSX, ReactNode } from "react";
2
+ type InputAttributes = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "className" | "children">;
3
+ /** A native checkbox and its text in one label: the row is the 44px target,
4
+ * the box keeps its own size. */
5
+ export declare function Checkbox({ children, ...input }: InputAttributes & {
6
+ readonly children: ReactNode;
7
+ }): JSX.Element;
8
+ /** Related checkboxes under a legend, without the browser's fieldset outline. */
9
+ export declare function CheckboxGroup({ legend, disabled, children }: {
10
+ readonly legend: ReactNode;
11
+ readonly disabled?: boolean | undefined;
12
+ readonly children: ReactNode;
13
+ }): JSX.Element;
14
+ export {};
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** A native checkbox and its text in one label: the row is the 44px target,
3
+ * the box keeps its own size. */
4
+ export function Checkbox({
5
+ children,
6
+ ...input
7
+ }) {
8
+ return _jsxs("label", {
9
+ className: "mdbase-check",
10
+ children: [_jsx("input", {
11
+ ...input,
12
+ type: "checkbox",
13
+ className: "mdbase-checkbox"
14
+ }), _jsx("span", {
15
+ children: children
16
+ })]
17
+ });
18
+ }
19
+ /** Related checkboxes under a legend, without the browser's fieldset outline. */
20
+ export function CheckboxGroup({
21
+ legend,
22
+ disabled,
23
+ children
24
+ }) {
25
+ return _jsxs("fieldset", {
26
+ className: "mdbase-check-group",
27
+ disabled: disabled,
28
+ children: [_jsx("legend", {
29
+ children: legend
30
+ }), children]
31
+ });
32
+ }
@@ -3,7 +3,7 @@ export interface Command {
3
3
  readonly id: string;
4
4
  readonly label: string;
5
5
  readonly detail?: string | undefined;
6
- /** Commands show under their group's heading, in the order the groups first appear. */
6
+ /** Stable group ID; headings follow first appearance and may be presented by groupLabel. */
7
7
  readonly group: string;
8
8
  readonly keywords?: string | undefined;
9
9
  /** For example "mod+shift+f"; see shortcutLabel. */
@@ -27,11 +27,13 @@ export declare function matchingCommands(commands: readonly Command[], query: st
27
27
  * Find or run anything: every mdbase app opens it with Mod+K. Typing filters commands; the
28
28
  * arrows move, Enter runs, Shift+Enter runs a command's alternate, Escape closes.
29
29
  */
30
- export declare function CommandPalette({ open, commands, onClose, label, placeholder, showAll }: {
30
+ export declare function CommandPalette({ open, commands, onClose, label, placeholder, showAll, groupLabel }: {
31
31
  readonly open: boolean;
32
32
  readonly commands: readonly Command[];
33
33
  readonly onClose: () => void;
34
34
  readonly label?: string | undefined;
35
35
  readonly placeholder?: string | undefined;
36
36
  readonly showAll?: readonly string[] | undefined;
37
+ /** Presentation only: translate headings without changing group or showAll IDs. */
38
+ readonly groupLabel?: ((group: string) => string) | undefined;
37
39
  }): JSX.Element | null;