@fastrd/ui-vue 0.1.1 → 0.2.0
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/NOTICE.md +34 -2
- package/README.md +29 -1
- package/dist/feedback.d.ts +13 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +94 -0
- package/dist/primitives.d.ts +4 -0
- package/dist/workbench.d.ts +34 -0
- package/package.json +28 -8
package/NOTICE.md
CHANGED
|
@@ -11,8 +11,9 @@ value/options/change contract and delegates interaction to the native select.
|
|
|
11
11
|
|
|
12
12
|
The palette follows FastWrite's semantic colors and the approved FastTask
|
|
13
13
|
workbench design. Only generic UI code and semantic design values are included.
|
|
14
|
-
No private artwork, icons, logos,
|
|
15
|
-
|
|
14
|
+
No private artwork, icons, logos, application data, product-specific workflows
|
|
15
|
+
or brand assets are distributed in the UI packages. Documentation fonts are
|
|
16
|
+
licensed separately as described below. Upstream product repositories
|
|
16
17
|
retain ownership of their application-specific code and assets.
|
|
17
18
|
|
|
18
19
|
Source reference revisions: FastPPT `f6bf21f13445c2507719928fa68550552a8fcab2`
|
|
@@ -21,3 +22,34 @@ Source reference revisions: FastPPT `f6bf21f13445c2507719928fa68550552a8fcab2`
|
|
|
21
22
|
At both revisions, `primitives.ts` has Git blob
|
|
22
23
|
`c7123af5f8316bc0486bce5c99cfbb2e9ab3dc73` and `use-dialog-focus.ts` has
|
|
23
24
|
Git blob `3ffbb6c6c7bd5e3c96f2b8c05c5e008dcdfc4cab`.
|
|
25
|
+
|
|
26
|
+
# FastUI notices
|
|
27
|
+
|
|
28
|
+
FastUI is an MIT-licensed design foundation and component library extracted from
|
|
29
|
+
shared interface work in FastR-D products. Product data, authentication, assets
|
|
30
|
+
and application workflows are not included.
|
|
31
|
+
|
|
32
|
+
The design aligns with the 2026-11-11 FastWrite workbench: compact geometry,
|
|
33
|
+
semantic light/dark colors, typography, flat tabs, project lists and focus states.
|
|
34
|
+
|
|
35
|
+
Design research used the fixed checkouts in FastWrite/reference:
|
|
36
|
+
|
|
37
|
+
- assistant-ui/assistant-ui (MIT), f92028b466cba721846de6851e4dddb6c04ecfad:
|
|
38
|
+
composable surfaces and separation of display from runtime state.
|
|
39
|
+
- shadcn-ui/ui (MIT), 8ab27715521cbd4f87ae6886cd989cbb678f6de7:
|
|
40
|
+
semantic tokens, variants and explicit focus/disabled states.
|
|
41
|
+
- radix-ui/primitives (MIT), eb3a2b7091293e27df8f9ba906632d8daa3d81a3:
|
|
42
|
+
keyboard navigation, disabled-item handling and focus order.
|
|
43
|
+
- nextlevelbuilder/ui-ux-pro-max-skill (MIT), 6ae86f61f520d6ca229d2f40e070410638c908c5:
|
|
44
|
+
navigation, deep links and reduced-motion guidance.
|
|
45
|
+
- ayaka-notes/ayakaleaf-pro (AGPL-3.0), 6ce22039d19b516354dff2a8508dd67515ded9f0:
|
|
46
|
+
project-list layout and interaction research only.
|
|
47
|
+
|
|
48
|
+
The workbench implementations are original. No source from these reference libraries,
|
|
49
|
+
including the AGPL project, is copied into the distributed MIT packages. Exact
|
|
50
|
+
repository URLs and revisions are recorded in docs/reference-sources.json.
|
|
51
|
+
|
|
52
|
+
The documentation site self-hosts IBM Plex Sans Variable 5.3.0, Noto Sans SC
|
|
53
|
+
Variable 5.3.1 and JetBrains Mono Variable 5.3.0 through Fontsource. These fonts use
|
|
54
|
+
SIL Open Font License 1.1; their original license texts are shipped with the
|
|
55
|
+
site under docs/public/licenses/fonts/. Font files are not shipped in UI packages.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @fastrd/ui-vue
|
|
2
2
|
|
|
3
|
-
MIT · 0.
|
|
3
|
+
MIT · 0.2.0 · Vue ^3.5 peer · exact foundation 0.2.0 registry dependency.
|
|
4
4
|
|
|
5
5
|
```vue
|
|
6
6
|
<script setup>
|
|
@@ -49,3 +49,31 @@ modal behavior replaces the original custom focus trap.
|
|
|
49
49
|
|
|
50
50
|
Class names use fastui-*; no legacy fp-* aliases or application brands are
|
|
51
51
|
included. Application layouts and preview styles stay with the host.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Workbench components
|
|
55
|
+
|
|
56
|
+
`TabBar`: `tabs`, `activeId`, accessible `label`, `onSelect` (Vue `@select`),
|
|
57
|
+
optional `onClose` (Vue `@close`), `closeLabel(tab)` and `dirtyLabel`.
|
|
58
|
+
Each TabItem has a unique DOM `id`, string `label`, optional `controls`,
|
|
59
|
+
`disabled`, `dirty`, `preview`. Host panels use `role="tabpanel"`, `id=controls`
|
|
60
|
+
and `aria-labelledby=activeId`; host owns visibility and active selection after
|
|
61
|
+
close. Automatic activation uses Left/Right, Home/End, RTL and disabled skipping.
|
|
62
|
+
Closing restores focus to the remaining tabbable tab. Empty/all-disabled lists
|
|
63
|
+
have no tabbable tab. Use unique ids across every instance.
|
|
64
|
+
|
|
65
|
+
`SegmentedControl`: `options` ({value,label,disabled?}), `value`, `label`,
|
|
66
|
+
`onChange` (Vue `@change`). A controlled mutually exclusive pressed-button group;
|
|
67
|
+
uses normal Tab order, not radio semantics.
|
|
68
|
+
|
|
69
|
+
`Toolbar` requires `label`; it groups mixed actions/fields with native Tab order.
|
|
70
|
+
`StatusBar` defaults role=status. `Section`, `ListRow`, `Divider` provide surfaces,
|
|
71
|
+
row separators and structure. ListRow is a noninteractive div by default; the
|
|
72
|
+
host owns selection, links, data, search and sorting.
|
|
73
|
+
|
|
74
|
+
Compact control sizes xs/sm/md are 24/25/28px. Typography uses the FastWrite
|
|
75
|
+
Plex/Noto stack with system fallback; fonts are loaded by the host, not the package.
|
|
76
|
+
|
|
77
|
+
`Alert` and `Badge`: tone info/success/warning/danger. Alert defaults role=status,
|
|
78
|
+
or alert for danger. `Loading` exposes a status region and label (default Loading).
|
|
79
|
+
`EmptyState` takes title, default content and an optional `#action` slot.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type FunctionalComponent, type HTMLAttributes } from 'vue';
|
|
2
|
+
export type Tone = 'info' | 'success' | 'warning' | 'danger';
|
|
3
|
+
export interface FeedbackProps extends HTMLAttributes {
|
|
4
|
+
tone?: Tone;
|
|
5
|
+
}
|
|
6
|
+
export declare const Alert: FunctionalComponent<FeedbackProps>;
|
|
7
|
+
export declare const Badge: FunctionalComponent<FeedbackProps>;
|
|
8
|
+
export declare const EmptyState: FunctionalComponent<HTMLAttributes & {
|
|
9
|
+
title: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare const Loading: FunctionalComponent<HTMLAttributes & {
|
|
12
|
+
label?: string;
|
|
13
|
+
}>;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -106,6 +106,12 @@ var SelectOption = element("SelectOption", "option");
|
|
|
106
106
|
var RichText = (props, { attrs }) => h("div", mergeProps(attrs, { innerHTML: props.html }));
|
|
107
107
|
RichText.props = ["html"];
|
|
108
108
|
RichText.inheritAttrs = false;
|
|
109
|
+
var Section = element("Section", "section", "fastui-section");
|
|
110
|
+
var StatusBar = (props, { attrs, slots }) => h(props.as ?? "div", mergeProps({ role: "status" }, attrs, { class: "fastui-statusbar" }), slots.default?.());
|
|
111
|
+
StatusBar.props = ["as"];
|
|
112
|
+
StatusBar.inheritAttrs = false;
|
|
113
|
+
var Divider = element("Divider", "hr", "fastui-divider");
|
|
114
|
+
var ListRow = element("ListRow", "div", "fastui-list-row");
|
|
109
115
|
|
|
110
116
|
// packages/vue/src/interactive.ts
|
|
111
117
|
import {
|
|
@@ -193,9 +199,88 @@ Select.props = {
|
|
|
193
199
|
onChange: Function
|
|
194
200
|
};
|
|
195
201
|
Select.inheritAttrs = false;
|
|
202
|
+
|
|
203
|
+
// packages/vue/src/workbench.ts
|
|
204
|
+
import { h as h3, mergeProps as mergeProps3, nextTick } from "vue";
|
|
205
|
+
import { tabNavigationIndex } from "@fastrd/ui-foundation";
|
|
206
|
+
var TabBar = (props, { attrs }) => {
|
|
207
|
+
const focusId = props.tabs.find((tab) => tab.id === props.activeId && !tab.disabled)?.id ?? props.tabs.find((tab) => !tab.disabled)?.id;
|
|
208
|
+
return h3("div", mergeProps3(attrs, { class: ["fastui-tabs", props.onClose && "fastui-tabs--closable"], role: "group", "aria-label": `${props.label} controls` }), [
|
|
209
|
+
h3("div", { role: "tablist", "aria-label": props.label, "aria-owns": props.tabs.map((tab) => tab.id).join(" ") || void 0 }),
|
|
210
|
+
...props.tabs.map((tab, index) => h3("div", { key: tab.id, class: "fastui-tab-item" }, [
|
|
211
|
+
h3("button", {
|
|
212
|
+
type: "button",
|
|
213
|
+
id: tab.id,
|
|
214
|
+
role: "tab",
|
|
215
|
+
disabled: tab.disabled,
|
|
216
|
+
"aria-selected": tab.id === props.activeId,
|
|
217
|
+
"aria-controls": tab.controls,
|
|
218
|
+
tabindex: tab.id === focusId ? 0 : -1,
|
|
219
|
+
class: ["fastui-tab", tab.preview && "fastui-tab--preview"],
|
|
220
|
+
onClick: () => props.onSelect(tab.id),
|
|
221
|
+
onKeydown: (event) => {
|
|
222
|
+
if (event.isComposing) return;
|
|
223
|
+
const target = event.currentTarget;
|
|
224
|
+
const next = tabNavigationIndex(event.key, index, props.tabs, getComputedStyle(target).direction === "rtl");
|
|
225
|
+
if (next < 0) return;
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
props.onSelect(props.tabs[next].id);
|
|
228
|
+
const button = target.closest(".fastui-tabs")?.querySelectorAll('[role="tab"]')[next];
|
|
229
|
+
button?.focus();
|
|
230
|
+
button?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
231
|
+
}
|
|
232
|
+
}, [h3("span", { class: "fastui-tab-label" }, tab.label), tab.dirty ? h3("span", { class: "fastui-tab-dirty", role: "img", "aria-label": props.dirtyLabel ?? "Unsaved changes" }) : null]),
|
|
233
|
+
props.onClose ? h3("button", {
|
|
234
|
+
type: "button",
|
|
235
|
+
class: "fastui-tab-close",
|
|
236
|
+
disabled: tab.disabled,
|
|
237
|
+
"aria-label": props.closeLabel?.(tab) ?? `Close ${tab.label}`,
|
|
238
|
+
onClick: (event) => {
|
|
239
|
+
const root = event.currentTarget.closest(".fastui-tabs");
|
|
240
|
+
props.onClose?.(tab.id);
|
|
241
|
+
void nextTick(() => {
|
|
242
|
+
if (root.isConnected) root.querySelector('[role="tab"][tabindex="0"]')?.focus();
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}, "\xD7") : null
|
|
246
|
+
]))
|
|
247
|
+
]);
|
|
248
|
+
};
|
|
249
|
+
TabBar.props = ["tabs", "activeId", "onSelect", "label", "onClose", "closeLabel", "dirtyLabel"];
|
|
250
|
+
TabBar.inheritAttrs = false;
|
|
251
|
+
var SegmentedControl = (props, { attrs }) => h3("div", mergeProps3(attrs, { class: "fastui-segments", role: "group", "aria-label": props.label }), props.options.map((option) => h3("button", {
|
|
252
|
+
type: "button",
|
|
253
|
+
key: option.value,
|
|
254
|
+
class: "fastui-segment",
|
|
255
|
+
disabled: option.disabled,
|
|
256
|
+
"aria-pressed": option.value === props.value,
|
|
257
|
+
onClick: () => props.onChange(option.value)
|
|
258
|
+
}, option.label)));
|
|
259
|
+
SegmentedControl.props = ["options", "value", "onChange", "label"];
|
|
260
|
+
SegmentedControl.inheritAttrs = false;
|
|
261
|
+
var Toolbar = (props, { attrs, slots }) => h3("div", mergeProps3(attrs, { class: "fastui-toolbar", role: "group", "aria-label": props.label }), slots.default?.());
|
|
262
|
+
Toolbar.props = ["label"];
|
|
263
|
+
Toolbar.inheritAttrs = false;
|
|
264
|
+
|
|
265
|
+
// packages/vue/src/feedback.ts
|
|
266
|
+
import { h as h4, mergeProps as mergeProps4 } from "vue";
|
|
267
|
+
var Alert = (props, { attrs, slots }) => h4("div", mergeProps4({ role: props.tone === "danger" ? "alert" : "status" }, attrs, { class: ["fastui-alert", `fastui-tone-${props.tone ?? "info"}`] }), slots.default?.());
|
|
268
|
+
Alert.props = ["tone"];
|
|
269
|
+
Alert.inheritAttrs = false;
|
|
270
|
+
var Badge = (props, { attrs, slots }) => h4("span", mergeProps4(attrs, { class: ["fastui-badge", `fastui-tone-${props.tone ?? "info"}`] }), slots.default?.());
|
|
271
|
+
Badge.props = ["tone"];
|
|
272
|
+
Badge.inheritAttrs = false;
|
|
273
|
+
var EmptyState = (props, { attrs, slots }) => h4("div", mergeProps4(attrs, { class: "fastui-empty" }), [h4("h2", props.title), slots.default?.(), slots.action?.()]);
|
|
274
|
+
EmptyState.props = ["title"];
|
|
275
|
+
EmptyState.inheritAttrs = false;
|
|
276
|
+
var Loading = (props, { attrs }) => h4("div", mergeProps4({ role: "status" }, attrs, { class: "fastui-loading" }), [h4("span", { class: "fastui-spinner", "aria-hidden": "true" }), props.label ?? "Loading"]);
|
|
277
|
+
Loading.props = ["label"];
|
|
278
|
+
Loading.inheritAttrs = false;
|
|
196
279
|
export {
|
|
280
|
+
Alert,
|
|
197
281
|
Article,
|
|
198
282
|
Aside,
|
|
283
|
+
Badge,
|
|
199
284
|
Box,
|
|
200
285
|
Button,
|
|
201
286
|
Description,
|
|
@@ -203,6 +288,8 @@ export {
|
|
|
203
288
|
DialogSurface,
|
|
204
289
|
Disclosure,
|
|
205
290
|
DisclosureTrigger,
|
|
291
|
+
Divider,
|
|
292
|
+
EmptyState,
|
|
206
293
|
Field,
|
|
207
294
|
Fieldset,
|
|
208
295
|
Footer,
|
|
@@ -215,6 +302,8 @@ export {
|
|
|
215
302
|
Link,
|
|
216
303
|
List,
|
|
217
304
|
ListItem,
|
|
305
|
+
ListRow,
|
|
306
|
+
Loading,
|
|
218
307
|
Main,
|
|
219
308
|
Modal,
|
|
220
309
|
ModalBody,
|
|
@@ -222,11 +311,15 @@ export {
|
|
|
222
311
|
Panel,
|
|
223
312
|
Progress,
|
|
224
313
|
RichText,
|
|
314
|
+
Section,
|
|
315
|
+
SegmentedControl,
|
|
225
316
|
Select,
|
|
226
317
|
SelectField,
|
|
227
318
|
SelectOption,
|
|
319
|
+
StatusBar,
|
|
228
320
|
Svg,
|
|
229
321
|
SvgPath,
|
|
322
|
+
TabBar,
|
|
230
323
|
Table,
|
|
231
324
|
TableBody,
|
|
232
325
|
TableCaption,
|
|
@@ -238,5 +331,6 @@ export {
|
|
|
238
331
|
Text,
|
|
239
332
|
TextArea,
|
|
240
333
|
TextField,
|
|
334
|
+
Toolbar,
|
|
241
335
|
useDialogFocus
|
|
242
336
|
};
|
package/dist/primitives.d.ts
CHANGED
|
@@ -67,3 +67,7 @@ export declare const SelectOption: FunctionalComponent<ElementProps, {}, any, {}
|
|
|
67
67
|
export declare const RichText: FunctionalComponent<HTMLAttributes & {
|
|
68
68
|
html: string;
|
|
69
69
|
}>;
|
|
70
|
+
export declare const Section: FunctionalComponent<ElementProps, {}, any, {}>;
|
|
71
|
+
export declare const StatusBar: FunctionalComponent<ElementProps>;
|
|
72
|
+
export declare const Divider: FunctionalComponent<ElementProps, {}, any, {}>;
|
|
73
|
+
export declare const ListRow: FunctionalComponent<ElementProps, {}, any, {}>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type FunctionalComponent, type HTMLAttributes } from 'vue';
|
|
2
|
+
export interface TabItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
controls?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
dirty?: boolean;
|
|
8
|
+
preview?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface TabBarProps {
|
|
11
|
+
tabs: readonly TabItem[];
|
|
12
|
+
activeId: string | null;
|
|
13
|
+
onSelect: (id: string) => void;
|
|
14
|
+
label: string;
|
|
15
|
+
onClose?: (id: string) => void;
|
|
16
|
+
closeLabel?: (tab: TabItem) => string;
|
|
17
|
+
dirtyLabel?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const TabBar: FunctionalComponent<TabBarProps>;
|
|
20
|
+
export interface SegmentedOption {
|
|
21
|
+
value: string;
|
|
22
|
+
label: string;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface SegmentedControlProps {
|
|
26
|
+
options: readonly SegmentedOption[];
|
|
27
|
+
value: string;
|
|
28
|
+
onChange: (value: string) => void;
|
|
29
|
+
label: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const SegmentedControl: FunctionalComponent<SegmentedControlProps>;
|
|
32
|
+
export declare const Toolbar: FunctionalComponent<HTMLAttributes & {
|
|
33
|
+
label: string;
|
|
34
|
+
}>;
|
package/package.json
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastrd/ui-vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vue primitives and accessible native controls for FastUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/FastR-D/FastUI.git",
|
|
10
|
+
"directory": "packages/vue"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.md",
|
|
19
|
+
"NOTICE.md"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": [
|
|
22
|
+
"**/*.css"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@fastrd/ui-foundation": "0.2.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"vue": "^3.5.0"
|
|
29
|
+
},
|
|
13
30
|
"exports": {
|
|
14
|
-
".": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js"
|
|
34
|
+
},
|
|
15
35
|
"./styles.css": "./dist/styles.css"
|
|
16
36
|
}
|
|
17
37
|
}
|