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

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 (74) hide show
  1. package/README.md +168 -0
  2. package/css/collections.css +978 -0
  3. package/css/controls.css +33 -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/CollectionModeControl.d.ts +11 -0
  9. package/dist/CollectionModeControl.js +290 -0
  10. package/dist/CommandPalette.d.ts +4 -2
  11. package/dist/CommandPalette.js +275 -103
  12. package/dist/Dialog.js +72 -25
  13. package/dist/Feedback.js +763 -229
  14. package/dist/FeedbackMarkup.d.ts +1 -1
  15. package/dist/FeedbackMarkup.js +326 -125
  16. package/dist/FeedbackVerification.js +115 -68
  17. package/dist/SaveNotice.js +71 -28
  18. package/dist/Select.js +183 -135
  19. package/dist/SelectList.js +59 -14
  20. package/dist/ThemeSelect.js +55 -4
  21. package/dist/apps.d.ts +1 -0
  22. package/dist/apps.js +50 -29
  23. package/dist/brand.js +251 -68
  24. package/dist/codemirror.js +116 -87
  25. package/dist/collections/CollectionGate.d.ts +52 -0
  26. package/dist/collections/CollectionGate.js +456 -0
  27. package/dist/collections/CollectionSaveWarning.d.ts +10 -0
  28. package/dist/collections/CollectionSaveWarning.js +28 -0
  29. package/dist/collections/CollectionSwitcher.d.ts +51 -0
  30. package/dist/collections/CollectionSwitcher.js +598 -0
  31. package/dist/collections/SetupReview.d.ts +9 -0
  32. package/dist/collections/SetupReview.js +216 -0
  33. package/dist/collections/copy.d.ts +100 -0
  34. package/dist/collections/copy.js +937 -0
  35. package/dist/collections/dialogs.d.ts +27 -0
  36. package/dist/collections/dialogs.js +169 -0
  37. package/dist/collections/model.d.ts +146 -0
  38. package/dist/collections/model.js +337 -0
  39. package/dist/collections/mount.d.ts +19 -0
  40. package/dist/collections/mount.js +56 -0
  41. package/dist/collections/parts.d.ts +86 -0
  42. package/dist/collections/parts.js +439 -0
  43. package/dist/collections/testing.d.ts +19 -0
  44. package/dist/collections/testing.js +39 -0
  45. package/dist/collections/use-collection-ui.d.ts +140 -0
  46. package/dist/collections/use-collection-ui.js +236 -0
  47. package/dist/collections.d.ts +19 -0
  48. package/dist/collections.js +13 -0
  49. package/dist/feedback-data.d.ts +13 -12
  50. package/dist/feedback-data.js +365 -168
  51. package/dist/flock.d.ts +63 -0
  52. package/dist/flock.js +140 -0
  53. package/dist/i18n.d.ts +6 -0
  54. package/dist/i18n.js +71 -0
  55. package/dist/locales/de/messages.json +1 -0
  56. package/dist/locales/en/messages.json +1 -0
  57. package/dist/locales/es/messages.json +1 -0
  58. package/dist/locales/fr/messages.json +1 -0
  59. package/dist/locales/it/messages.json +1 -0
  60. package/dist/locales/ja/messages.json +1 -0
  61. package/dist/locales/ko/messages.json +1 -0
  62. package/dist/locales/pt/messages.json +1 -0
  63. package/dist/locales/ru/messages.json +1 -0
  64. package/dist/locales/zh/messages.json +1 -0
  65. package/dist/mark-activity.js +82 -75
  66. package/dist/permission-selection.js +27 -17
  67. package/dist/popover.js +138 -130
  68. package/dist/screens.d.ts +20 -3
  69. package/dist/screens.js +275 -6
  70. package/dist/select-model.js +34 -28
  71. package/dist/text-diff.d.ts +1 -0
  72. package/dist/text-diff.js +106 -74
  73. package/dist/theme.js +34 -40
  74. package/package.json +56 -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. */
@@ -761,3 +786,10 @@ textarea.mdbase-field {
761
786
  transition: none;
762
787
  }
763
788
  }
789
+
790
+ /* Mode changes stay inline; progress is text, never an optimistic toggle. */
791
+ .mdbase-collection-mode { display: grid; gap: var(--space-3); min-inline-size: 0; }
792
+ .mdbase-collection-mode :is(h3, p, ul) { margin-block: 0; }
793
+ .mdbase-collection-mode h3 { font-size: var(--font-size-section); font-weight: var(--font-weight-medium); }
794
+ .mdbase-collection-mode > div:last-child { display: flex; flex-wrap: wrap; gap: var(--space-2); }
795
+ .mdbase-collection-mode code { overflow-wrap: anywhere; }
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
+ }
@@ -0,0 +1,11 @@
1
+ type Mode = "private" | "cloud_copy";
2
+ /** Owner controls only. Visibility follows actual executor registration, not a
3
+ * mode flag. Browser intent cannot produce native key/source consent. */
4
+ export declare function CollectionModeControl({ collectionId, load, change, cancel, native }: {
5
+ collectionId: string;
6
+ load(): Promise<unknown>;
7
+ change(target: Mode, requestId: string): Promise<unknown>;
8
+ native?: boolean;
9
+ cancel?(target: Mode, requestId: string): Promise<unknown>;
10
+ }): import("react").JSX.Element | null;
11
+ export {};