@c9up/nebula 0.1.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/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/adapters/css.d.ts +33 -0
- package/dist/adapters/css.js +67 -0
- package/dist/adapters/index.d.ts +16 -0
- package/dist/adapters/index.js +27 -0
- package/dist/adapters/tailwind.d.ts +25 -0
- package/dist/adapters/tailwind.js +122 -0
- package/dist/adapters/types.d.ts +49 -0
- package/dist/adapters/types.js +21 -0
- package/dist/adapters/unocss.d.ts +17 -0
- package/dist/adapters/unocss.js +107 -0
- package/dist/atoms/AspectRatio.d.ts +17 -0
- package/dist/atoms/AspectRatio.js +19 -0
- package/dist/atoms/Avatar.d.ts +23 -0
- package/dist/atoms/Avatar.js +46 -0
- package/dist/atoms/Badge.d.ts +25 -0
- package/dist/atoms/Badge.js +28 -0
- package/dist/atoms/Button.d.ts +58 -0
- package/dist/atoms/Button.js +67 -0
- package/dist/atoms/Checkbox.d.ts +41 -0
- package/dist/atoms/Checkbox.js +47 -0
- package/dist/atoms/Input.d.ts +36 -0
- package/dist/atoms/Input.js +50 -0
- package/dist/atoms/Kbd.d.ts +14 -0
- package/dist/atoms/Kbd.js +17 -0
- package/dist/atoms/Label.d.ts +24 -0
- package/dist/atoms/Label.js +26 -0
- package/dist/atoms/Marker.d.ts +42 -0
- package/dist/atoms/Marker.js +55 -0
- package/dist/atoms/NativeSelect.d.ts +43 -0
- package/dist/atoms/NativeSelect.js +81 -0
- package/dist/atoms/Progress.d.ts +21 -0
- package/dist/atoms/Progress.js +48 -0
- package/dist/atoms/ScrollArea.d.ts +28 -0
- package/dist/atoms/ScrollArea.js +37 -0
- package/dist/atoms/Separator.d.ts +16 -0
- package/dist/atoms/Separator.js +21 -0
- package/dist/atoms/Skeleton.d.ts +13 -0
- package/dist/atoms/Skeleton.js +18 -0
- package/dist/atoms/Slider.d.ts +30 -0
- package/dist/atoms/Slider.js +40 -0
- package/dist/atoms/Spinner.d.ts +17 -0
- package/dist/atoms/Spinner.js +20 -0
- package/dist/atoms/Switch.d.ts +24 -0
- package/dist/atoms/Switch.js +41 -0
- package/dist/atoms/Textarea.d.ts +28 -0
- package/dist/atoms/Textarea.js +36 -0
- package/dist/atoms/Toggle.d.ts +38 -0
- package/dist/atoms/Toggle.js +45 -0
- package/dist/atoms/index.d.ts +32 -0
- package/dist/atoms/index.js +32 -0
- package/dist/cli/add.d.ts +44 -0
- package/dist/cli/add.js +99 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/registry.d.ts +31 -0
- package/dist/cli/registry.js +83 -0
- package/dist/config.d.ts +81 -0
- package/dist/config.js +43 -0
- package/dist/configure.d.ts +34 -0
- package/dist/configure.js +85 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +41 -0
- package/dist/lib/children.d.ts +28 -0
- package/dist/lib/children.js +27 -0
- package/dist/lib/cn.d.ts +13 -0
- package/dist/lib/cn.js +13 -0
- package/dist/lib/cva.d.ts +83 -0
- package/dist/lib/cva.js +178 -0
- package/dist/lib/icons.d.ts +44 -0
- package/dist/lib/icons.js +79 -0
- package/dist/lib/id.d.ts +31 -0
- package/dist/lib/id.js +41 -0
- package/dist/lib/index.d.ts +12 -0
- package/dist/lib/index.js +12 -0
- package/dist/lib/motion.d.ts +31 -0
- package/dist/lib/motion.js +39 -0
- package/dist/lib/props.d.ts +48 -0
- package/dist/lib/props.js +59 -0
- package/dist/lib/styled.d.ts +31 -0
- package/dist/lib/styled.js +39 -0
- package/dist/molecules/Accordion.d.ts +35 -0
- package/dist/molecules/Accordion.js +117 -0
- package/dist/molecules/Alert.d.ts +30 -0
- package/dist/molecules/Alert.js +35 -0
- package/dist/molecules/Attachment.d.ts +39 -0
- package/dist/molecules/Attachment.js +103 -0
- package/dist/molecules/Breadcrumb.d.ts +29 -0
- package/dist/molecules/Breadcrumb.js +73 -0
- package/dist/molecules/Bubble.d.ts +56 -0
- package/dist/molecules/Bubble.js +92 -0
- package/dist/molecules/ButtonGroup.d.ts +23 -0
- package/dist/molecules/ButtonGroup.js +30 -0
- package/dist/molecules/Card.d.ts +17 -0
- package/dist/molecules/Card.js +17 -0
- package/dist/molecules/Collapsible.d.ts +40 -0
- package/dist/molecules/Collapsible.js +71 -0
- package/dist/molecules/Empty.d.ts +16 -0
- package/dist/molecules/Empty.js +16 -0
- package/dist/molecules/Field.d.ts +54 -0
- package/dist/molecules/Field.js +82 -0
- package/dist/molecules/InputGroup.d.ts +33 -0
- package/dist/molecules/InputGroup.js +35 -0
- package/dist/molecules/InputOTP.d.ts +33 -0
- package/dist/molecules/InputOTP.js +140 -0
- package/dist/molecules/Item.d.ts +41 -0
- package/dist/molecules/Item.js +48 -0
- package/dist/molecules/Message.d.ts +43 -0
- package/dist/molecules/Message.js +82 -0
- package/dist/molecules/Pagination.d.ts +41 -0
- package/dist/molecules/Pagination.js +144 -0
- package/dist/molecules/RadioGroup.d.ts +32 -0
- package/dist/molecules/RadioGroup.js +65 -0
- package/dist/molecules/Resizable.d.ts +37 -0
- package/dist/molecules/Resizable.js +136 -0
- package/dist/molecules/Table.d.ts +37 -0
- package/dist/molecules/Table.js +78 -0
- package/dist/molecules/Tabs.d.ts +37 -0
- package/dist/molecules/Tabs.js +109 -0
- package/dist/molecules/ToggleGroup.d.ts +37 -0
- package/dist/molecules/ToggleGroup.js +68 -0
- package/dist/molecules/Typography.d.ts +26 -0
- package/dist/molecules/Typography.js +67 -0
- package/dist/molecules/index.d.ts +37 -0
- package/dist/molecules/index.js +37 -0
- package/dist/organisms/AlertDialog.d.ts +40 -0
- package/dist/organisms/AlertDialog.js +104 -0
- package/dist/organisms/Calendar.d.ts +96 -0
- package/dist/organisms/Calendar.js +353 -0
- package/dist/organisms/Carousel.d.ts +29 -0
- package/dist/organisms/Carousel.js +128 -0
- package/dist/organisms/Chart.d.ts +55 -0
- package/dist/organisms/Chart.js +189 -0
- package/dist/organisms/Combobox.d.ts +41 -0
- package/dist/organisms/Combobox.js +102 -0
- package/dist/organisms/Command.d.ts +46 -0
- package/dist/organisms/Command.js +171 -0
- package/dist/organisms/CommandDialog.d.ts +42 -0
- package/dist/organisms/CommandDialog.js +116 -0
- package/dist/organisms/ContextMenu.d.ts +29 -0
- package/dist/organisms/ContextMenu.js +102 -0
- package/dist/organisms/DataTable.d.ts +50 -0
- package/dist/organisms/DataTable.js +257 -0
- package/dist/organisms/DatePicker.d.ts +34 -0
- package/dist/organisms/DatePicker.js +115 -0
- package/dist/organisms/DateRangePicker.d.ts +34 -0
- package/dist/organisms/DateRangePicker.js +122 -0
- package/dist/organisms/Dialog.d.ts +50 -0
- package/dist/organisms/Dialog.js +116 -0
- package/dist/organisms/Drawer.d.ts +40 -0
- package/dist/organisms/Drawer.js +131 -0
- package/dist/organisms/DropdownMenu.d.ts +30 -0
- package/dist/organisms/DropdownMenu.js +91 -0
- package/dist/organisms/Form.d.ts +114 -0
- package/dist/organisms/Form.js +151 -0
- package/dist/organisms/HoverCard.d.ts +30 -0
- package/dist/organisms/HoverCard.js +68 -0
- package/dist/organisms/Menubar.d.ts +28 -0
- package/dist/organisms/Menubar.js +147 -0
- package/dist/organisms/MessageScroller.d.ts +36 -0
- package/dist/organisms/MessageScroller.js +124 -0
- package/dist/organisms/NavigationMenu.d.ts +41 -0
- package/dist/organisms/NavigationMenu.js +139 -0
- package/dist/organisms/Popover.d.ts +33 -0
- package/dist/organisms/Popover.js +58 -0
- package/dist/organisms/Questionnaire.d.ts +74 -0
- package/dist/organisms/Questionnaire.js +235 -0
- package/dist/organisms/Select.d.ts +47 -0
- package/dist/organisms/Select.js +250 -0
- package/dist/organisms/Sheet.d.ts +34 -0
- package/dist/organisms/Sheet.js +107 -0
- package/dist/organisms/Sidebar.d.ts +100 -0
- package/dist/organisms/Sidebar.js +228 -0
- package/dist/organisms/Toaster.d.ts +67 -0
- package/dist/organisms/Toaster.js +189 -0
- package/dist/organisms/Tooltip.d.ts +39 -0
- package/dist/organisms/Tooltip.js +94 -0
- package/dist/organisms/index.d.ts +40 -0
- package/dist/organisms/index.js +40 -0
- package/dist/primitives/controllable.d.ts +38 -0
- package/dist/primitives/controllable.js +43 -0
- package/dist/primitives/dismissable.d.ts +53 -0
- package/dist/primitives/dismissable.js +158 -0
- package/dist/primitives/floating.d.ts +116 -0
- package/dist/primitives/floating.js +289 -0
- package/dist/primitives/floatingSurface.d.ts +56 -0
- package/dist/primitives/floatingSurface.js +142 -0
- package/dist/primitives/focusTrap.d.ts +47 -0
- package/dist/primitives/focusTrap.js +101 -0
- package/dist/primitives/focusable.d.ts +53 -0
- package/dist/primitives/focusable.js +109 -0
- package/dist/primitives/index.d.ts +27 -0
- package/dist/primitives/index.js +27 -0
- package/dist/primitives/menu.d.ts +102 -0
- package/dist/primitives/menu.js +356 -0
- package/dist/primitives/modalSurface.d.ts +54 -0
- package/dist/primitives/modalSurface.js +146 -0
- package/dist/primitives/portal.d.ts +34 -0
- package/dist/primitives/portal.js +42 -0
- package/dist/primitives/presence.d.ts +45 -0
- package/dist/primitives/presence.js +152 -0
- package/dist/primitives/rovingFocus.d.ts +47 -0
- package/dist/primitives/rovingFocus.js +176 -0
- package/dist/primitives/scrollLock.d.ts +21 -0
- package/dist/primitives/scrollLock.js +75 -0
- package/dist/primitives/typeahead.d.ts +39 -0
- package/dist/primitives/typeahead.js +123 -0
- package/dist/templates/AppShell.d.ts +30 -0
- package/dist/templates/AppShell.js +42 -0
- package/dist/templates/AuthLayout.d.ts +23 -0
- package/dist/templates/AuthLayout.js +42 -0
- package/dist/templates/SettingsLayout.d.ts +27 -0
- package/dist/templates/SettingsLayout.js +42 -0
- package/dist/templates/index.d.ts +16 -0
- package/dist/templates/index.js +16 -0
- package/nebula.css +2 -0
- package/package.json +92 -0
- package/registry.json +1107 -0
- package/src/adapters/css.ts +75 -0
- package/src/adapters/index.ts +40 -0
- package/src/adapters/tailwind.ts +134 -0
- package/src/adapters/types.ts +53 -0
- package/src/adapters/unocss.ts +116 -0
- package/src/atoms/AspectRatio.ts +28 -0
- package/src/atoms/Avatar.ts +67 -0
- package/src/atoms/Badge.ts +46 -0
- package/src/atoms/Button.ts +96 -0
- package/src/atoms/Checkbox.ts +76 -0
- package/src/atoms/Input.ts +76 -0
- package/src/atoms/Kbd.ts +28 -0
- package/src/atoms/Label.ts +38 -0
- package/src/atoms/Marker.ts +79 -0
- package/src/atoms/NativeSelect.ts +115 -0
- package/src/atoms/Progress.ts +64 -0
- package/src/atoms/ScrollArea.ts +53 -0
- package/src/atoms/Separator.ts +36 -0
- package/src/atoms/Skeleton.ts +24 -0
- package/src/atoms/Slider.ts +65 -0
- package/src/atoms/Spinner.ts +28 -0
- package/src/atoms/Switch.ts +56 -0
- package/src/atoms/Textarea.ts +58 -0
- package/src/atoms/Toggle.ts +67 -0
- package/src/atoms/index.ts +62 -0
- package/src/cli/add.ts +126 -0
- package/src/cli/index.ts +22 -0
- package/src/cli/registry.ts +116 -0
- package/src/config.ts +102 -0
- package/src/configure.ts +127 -0
- package/src/index.ts +53 -0
- package/src/lib/children.ts +43 -0
- package/src/lib/cn.ts +14 -0
- package/src/lib/cva.ts +249 -0
- package/src/lib/icons.ts +138 -0
- package/src/lib/id.ts +44 -0
- package/src/lib/index.ts +21 -0
- package/src/lib/motion.ts +48 -0
- package/src/lib/props.ts +83 -0
- package/src/lib/styled.ts +60 -0
- package/src/molecules/Accordion.ts +161 -0
- package/src/molecules/Alert.ts +58 -0
- package/src/molecules/Attachment.ts +132 -0
- package/src/molecules/Breadcrumb.ts +107 -0
- package/src/molecules/Bubble.ts +134 -0
- package/src/molecules/ButtonGroup.ts +55 -0
- package/src/molecules/Card.ts +42 -0
- package/src/molecules/Collapsible.ts +89 -0
- package/src/molecules/Empty.ts +43 -0
- package/src/molecules/Field.ts +131 -0
- package/src/molecules/InputGroup.ts +62 -0
- package/src/molecules/InputOTP.ts +166 -0
- package/src/molecules/Item.ts +89 -0
- package/src/molecules/Message.ts +127 -0
- package/src/molecules/Pagination.ts +197 -0
- package/src/molecules/RadioGroup.ts +104 -0
- package/src/molecules/Resizable.ts +166 -0
- package/src/molecules/Table.ts +118 -0
- package/src/molecules/Tabs.ts +148 -0
- package/src/molecules/ToggleGroup.ts +109 -0
- package/src/molecules/Typography.ts +94 -0
- package/src/molecules/index.ts +144 -0
- package/src/organisms/AlertDialog.ts +133 -0
- package/src/organisms/Calendar.ts +470 -0
- package/src/organisms/Carousel.ts +161 -0
- package/src/organisms/Chart.ts +257 -0
- package/src/organisms/Combobox.ts +140 -0
- package/src/organisms/Command.ts +211 -0
- package/src/organisms/CommandDialog.ts +149 -0
- package/src/organisms/ContextMenu.ts +125 -0
- package/src/organisms/DataTable.ts +328 -0
- package/src/organisms/DatePicker.ts +141 -0
- package/src/organisms/DateRangePicker.ts +149 -0
- package/src/organisms/Dialog.ts +162 -0
- package/src/organisms/Drawer.ts +167 -0
- package/src/organisms/DropdownMenu.ts +112 -0
- package/src/organisms/Form.ts +217 -0
- package/src/organisms/HoverCard.ts +92 -0
- package/src/organisms/Menubar.ts +167 -0
- package/src/organisms/MessageScroller.ts +145 -0
- package/src/organisms/NavigationMenu.ts +184 -0
- package/src/organisms/Popover.ts +84 -0
- package/src/organisms/Questionnaire.ts +322 -0
- package/src/organisms/Select.ts +286 -0
- package/src/organisms/Sheet.ts +144 -0
- package/src/organisms/Sidebar.ts +336 -0
- package/src/organisms/Toaster.ts +246 -0
- package/src/organisms/Tooltip.ts +124 -0
- package/src/organisms/index.ts +134 -0
- package/src/primitives/controllable.ts +64 -0
- package/src/primitives/dismissable.ts +195 -0
- package/src/primitives/floating.ts +453 -0
- package/src/primitives/floatingSurface.ts +209 -0
- package/src/primitives/focusTrap.ts +132 -0
- package/src/primitives/focusable.ts +108 -0
- package/src/primitives/index.ts +88 -0
- package/src/primitives/menu.ts +491 -0
- package/src/primitives/modalSurface.ts +199 -0
- package/src/primitives/portal.ts +61 -0
- package/src/primitives/presence.ts +185 -0
- package/src/primitives/rovingFocus.ts +212 -0
- package/src/primitives/scrollLock.ts +79 -0
- package/src/primitives/typeahead.ts +156 -0
- package/src/templates/AppShell.ts +56 -0
- package/src/templates/AuthLayout.ts +66 -0
- package/src/templates/SettingsLayout.ts +63 -0
- package/src/templates/index.ts +21 -0
- package/theme.css +151 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner — an indeterminate activity indicator.
|
|
3
|
+
*
|
|
4
|
+
* `role="status"` with a visually hidden label, so a screen reader announces
|
|
5
|
+
* that something is loading. A bare spinning glyph is invisible to a reader
|
|
6
|
+
* and the user is left with silence where a sighted user sees motion.
|
|
7
|
+
*
|
|
8
|
+
* `motion-reduce:animate-none` stops the rotation for users who asked the
|
|
9
|
+
* system to reduce motion. The glyph stays, so the indicator does not vanish.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { component, html } from "@c9up/aurora";
|
|
13
|
+
import { cn } from "../lib/cn.js";
|
|
14
|
+
import { LoaderIcon } from "../lib/icons.js";
|
|
15
|
+
import { type Reactive, read, readOr } from "../lib/props.js";
|
|
16
|
+
|
|
17
|
+
export interface SpinnerProps {
|
|
18
|
+
/** Announced to assistive technology. Defaults to `"Loading"`. */
|
|
19
|
+
label?: Reactive<string>;
|
|
20
|
+
class?: Reactive<string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Spinner = component<SpinnerProps>((props) => {
|
|
24
|
+
return html`<span data-slot="spinner" role="status" class="inline-flex">
|
|
25
|
+
${LoaderIcon({ class: cn("size-4 animate-spin motion-reduce:animate-none", read(props.class)) })}
|
|
26
|
+
<span class="sr-only">${() => readOr(props.label, "Loading")}</span>
|
|
27
|
+
</span>`;
|
|
28
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch — an on/off toggle that applies immediately.
|
|
3
|
+
*
|
|
4
|
+
* Same native-input approach as Checkbox, with `role="switch"` so assistive
|
|
5
|
+
* technology announces "on/off" rather than "checked". The distinction is not
|
|
6
|
+
* cosmetic: a checkbox is a choice you confirm by submitting, a switch is a
|
|
7
|
+
* setting that takes effect the moment you flip it.
|
|
8
|
+
*
|
|
9
|
+
* The thumb is a sibling translated by `peer-checked:`, not a pseudo-element,
|
|
10
|
+
* so the travel distance stays expressible in utility classes and a user can
|
|
11
|
+
* retune the size without leaving the class string.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { component, html } from "@c9up/aurora";
|
|
15
|
+
import { cn } from "../lib/cn.js";
|
|
16
|
+
import { accessor, type Reactive, read } from "../lib/props.js";
|
|
17
|
+
|
|
18
|
+
export const switchTrackClasses =
|
|
19
|
+
"peer absolute inset-0 size-full appearance-none rounded-full border border-transparent bg-input shadow-xs outline-none transition-colors dark:bg-input/80 checked:bg-primary focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50";
|
|
20
|
+
|
|
21
|
+
export interface SwitchProps {
|
|
22
|
+
id?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
checked?: Reactive<boolean>;
|
|
25
|
+
disabled?: Reactive<boolean>;
|
|
26
|
+
label?: Reactive<string | undefined>;
|
|
27
|
+
class?: Reactive<string>;
|
|
28
|
+
onCheckedChange?: (checked: boolean, event: Event) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function inputChecked(event: Event): boolean {
|
|
32
|
+
const target = event.target;
|
|
33
|
+
return target instanceof HTMLInputElement ? target.checked : false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Switch = component<SwitchProps>((props) => {
|
|
37
|
+
return html`<span
|
|
38
|
+
data-slot="switch"
|
|
39
|
+
class="${() => cn("relative inline-flex h-[1.15rem] w-8 shrink-0 items-center", read(props.class))}"
|
|
40
|
+
>
|
|
41
|
+
<input
|
|
42
|
+
type="checkbox"
|
|
43
|
+
role="switch"
|
|
44
|
+
id="${props.id}"
|
|
45
|
+
name="${props.name}"
|
|
46
|
+
aria-label="${accessor(props.label, undefined)}"
|
|
47
|
+
?disabled="${accessor(props.disabled, false)}"
|
|
48
|
+
.checked="${accessor(props.checked, false)}"
|
|
49
|
+
class="${switchTrackClasses}"
|
|
50
|
+
@change="${(event: Event) => props.onCheckedChange?.(inputChecked(event), event)}"
|
|
51
|
+
/>
|
|
52
|
+
<span
|
|
53
|
+
class="pointer-events-none ml-px block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform peer-checked:translate-x-[calc(100%-2px)] dark:peer-checked:bg-primary-foreground"
|
|
54
|
+
></span>
|
|
55
|
+
</span>`;
|
|
56
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Textarea — a multi-line text field.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `Input` deliberately, down to the prop names: a form field should
|
|
5
|
+
* not need a different shape because it grew a second line. The `field-sizing-
|
|
6
|
+
* content` utility is the one addition — it lets the browser grow the box with
|
|
7
|
+
* its content, which used to require a resize observer and a scroll-height
|
|
8
|
+
* measurement on every keystroke.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { component, html } from "@c9up/aurora";
|
|
12
|
+
import { cn } from "../lib/cn.js";
|
|
13
|
+
import { accessor, type Reactive, read } from "../lib/props.js";
|
|
14
|
+
|
|
15
|
+
export const textareaClasses =
|
|
16
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm";
|
|
17
|
+
|
|
18
|
+
export interface TextareaProps {
|
|
19
|
+
id?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
value?: Reactive<string>;
|
|
22
|
+
placeholder?: Reactive<string>;
|
|
23
|
+
rows?: Reactive<number>;
|
|
24
|
+
disabled?: Reactive<boolean>;
|
|
25
|
+
readonly?: Reactive<boolean>;
|
|
26
|
+
required?: Reactive<boolean>;
|
|
27
|
+
invalid?: Reactive<boolean>;
|
|
28
|
+
describedBy?: Reactive<string | undefined>;
|
|
29
|
+
class?: Reactive<string>;
|
|
30
|
+
onInput?: (value: string, event: Event) => void;
|
|
31
|
+
onChange?: (value: string, event: Event) => void;
|
|
32
|
+
onBlur?: (event: FocusEvent) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function inputValue(event: Event): string {
|
|
36
|
+
const target = event.target;
|
|
37
|
+
return target instanceof HTMLTextAreaElement ? target.value : "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Textarea = component<TextareaProps>((props) => {
|
|
41
|
+
return html`<textarea
|
|
42
|
+
data-slot="textarea"
|
|
43
|
+
id="${props.id}"
|
|
44
|
+
name="${props.name}"
|
|
45
|
+
placeholder="${accessor(props.placeholder, undefined)}"
|
|
46
|
+
rows="${accessor(props.rows, undefined)}"
|
|
47
|
+
aria-invalid="${() => (read(props.invalid) === true ? "true" : undefined)}"
|
|
48
|
+
aria-describedby="${accessor(props.describedBy, undefined)}"
|
|
49
|
+
?disabled="${accessor(props.disabled, false)}"
|
|
50
|
+
?readonly="${accessor(props.readonly, false)}"
|
|
51
|
+
?required="${accessor(props.required, false)}"
|
|
52
|
+
.value="${accessor(props.value, "")}"
|
|
53
|
+
class="${() => cn(textareaClasses, read(props.class))}"
|
|
54
|
+
@input="${(event: Event) => props.onInput?.(inputValue(event), event)}"
|
|
55
|
+
@change="${(event: Event) => props.onChange?.(inputValue(event), event)}"
|
|
56
|
+
@blur="${props.onBlur}"
|
|
57
|
+
></textarea>`;
|
|
58
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toggle — a button that stays pressed.
|
|
3
|
+
*
|
|
4
|
+
* `aria-pressed` rather than a checkbox: the control is a button whose effect
|
|
5
|
+
* applies immediately (bold, mute, filter on), not a value being collected for
|
|
6
|
+
* submission. Screen readers announce it as "toggle button, pressed", which is
|
|
7
|
+
* what the user needs to hear.
|
|
8
|
+
*
|
|
9
|
+
* The variants are exported for ToggleGroup, which reuses them so a grouped
|
|
10
|
+
* toggle and a standalone one cannot drift apart.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { component, html } from "@c9up/aurora";
|
|
14
|
+
import { type Slot, slot } from "../lib/children.js";
|
|
15
|
+
import { cva, type VariantProps } from "../lib/cva.js";
|
|
16
|
+
import { accessor, type Reactive, read, readOr } from "../lib/props.js";
|
|
17
|
+
|
|
18
|
+
export const toggleVariants = cva(
|
|
19
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-pressed:bg-accent aria-pressed:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 whitespace-nowrap",
|
|
20
|
+
{
|
|
21
|
+
variants: {
|
|
22
|
+
variant: {
|
|
23
|
+
default: "bg-transparent",
|
|
24
|
+
outline:
|
|
25
|
+
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
|
|
26
|
+
},
|
|
27
|
+
size: {
|
|
28
|
+
default: "h-9 min-w-9 px-2",
|
|
29
|
+
sm: "h-8 min-w-8 px-1.5",
|
|
30
|
+
lg: "h-10 min-w-10 px-2.5",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultVariants: { variant: "default", size: "default" },
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export type ToggleVariants = VariantProps<typeof toggleVariants>;
|
|
38
|
+
|
|
39
|
+
export interface ToggleProps {
|
|
40
|
+
children?: Slot;
|
|
41
|
+
pressed?: Reactive<boolean>;
|
|
42
|
+
variant?: Reactive<ToggleVariants["variant"]>;
|
|
43
|
+
size?: Reactive<ToggleVariants["size"]>;
|
|
44
|
+
disabled?: Reactive<boolean>;
|
|
45
|
+
label?: Reactive<string | undefined>;
|
|
46
|
+
value?: string;
|
|
47
|
+
class?: Reactive<string>;
|
|
48
|
+
onPressedChange?: (pressed: boolean) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const Toggle = component<ToggleProps>((props) => {
|
|
52
|
+
return html`<button
|
|
53
|
+
type="button"
|
|
54
|
+
data-slot="toggle"
|
|
55
|
+
data-value="${props.value}"
|
|
56
|
+
aria-pressed="${() => (readOr(props.pressed, false) ? "true" : "false")}"
|
|
57
|
+
aria-label="${accessor(props.label, undefined)}"
|
|
58
|
+
?disabled="${accessor(props.disabled, false)}"
|
|
59
|
+
class="${() =>
|
|
60
|
+
toggleVariants({
|
|
61
|
+
variant: read(props.variant),
|
|
62
|
+
size: read(props.size),
|
|
63
|
+
class: read(props.class),
|
|
64
|
+
})}"
|
|
65
|
+
@click="${() => props.onPressedChange?.(!readOr(props.pressed, false))}"
|
|
66
|
+
>${slot(props.children)}</button>`;
|
|
67
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atoms — one element, no nebula components inside.
|
|
3
|
+
*
|
|
4
|
+
* The dividing line the whole library is organised on: an atom renders its own
|
|
5
|
+
* markup and composes nothing from nebula. It may hold local state (Avatar
|
|
6
|
+
* tracks whether its image loaded) and it may be interactive (Checkbox,
|
|
7
|
+
* Slider), but the moment it reaches for another nebula component it belongs a
|
|
8
|
+
* layer up.
|
|
9
|
+
*
|
|
10
|
+
* Applied strictly, that rule puts things where a reader expects them. Card is
|
|
11
|
+
* a molecule despite being simpler than Slider, because it assembles parts.
|
|
12
|
+
* Slider is an atom despite being interactive, because it is one input.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export { AspectRatio, type AspectRatioProps } from "./AspectRatio.js";
|
|
16
|
+
export { Avatar, type AvatarProps } from "./Avatar.js";
|
|
17
|
+
export {
|
|
18
|
+
Badge,
|
|
19
|
+
type BadgeProps,
|
|
20
|
+
type BadgeVariants,
|
|
21
|
+
badgeVariants,
|
|
22
|
+
} from "./Badge.js";
|
|
23
|
+
export {
|
|
24
|
+
Button,
|
|
25
|
+
type ButtonProps,
|
|
26
|
+
type ButtonVariants,
|
|
27
|
+
buttonVariants,
|
|
28
|
+
} from "./Button.js";
|
|
29
|
+
export { Checkbox, type CheckboxProps, checkboxClasses } from "./Checkbox.js";
|
|
30
|
+
export { Input, type InputProps, inputClasses } from "./Input.js";
|
|
31
|
+
export { Kbd, type KbdProps } from "./Kbd.js";
|
|
32
|
+
export { Label, type LabelProps, labelClasses } from "./Label.js";
|
|
33
|
+
export {
|
|
34
|
+
Marker,
|
|
35
|
+
type MarkerProps,
|
|
36
|
+
type MarkerVariants,
|
|
37
|
+
markerVariants,
|
|
38
|
+
} from "./Marker.js";
|
|
39
|
+
export {
|
|
40
|
+
NativeSelect,
|
|
41
|
+
type NativeSelectOption,
|
|
42
|
+
type NativeSelectProps,
|
|
43
|
+
nativeSelectClasses,
|
|
44
|
+
} from "./NativeSelect.js";
|
|
45
|
+
export { Progress, type ProgressProps } from "./Progress.js";
|
|
46
|
+
export {
|
|
47
|
+
ScrollArea,
|
|
48
|
+
type ScrollAreaProps,
|
|
49
|
+
scrollAreaClasses,
|
|
50
|
+
} from "./ScrollArea.js";
|
|
51
|
+
export { Separator, type SeparatorProps } from "./Separator.js";
|
|
52
|
+
export { Skeleton, type SkeletonProps } from "./Skeleton.js";
|
|
53
|
+
export { Slider, type SliderProps, sliderClasses } from "./Slider.js";
|
|
54
|
+
export { Spinner, type SpinnerProps } from "./Spinner.js";
|
|
55
|
+
export { Switch, type SwitchProps, switchTrackClasses } from "./Switch.js";
|
|
56
|
+
export { Textarea, type TextareaProps, textareaClasses } from "./Textarea.js";
|
|
57
|
+
export {
|
|
58
|
+
Toggle,
|
|
59
|
+
type ToggleProps,
|
|
60
|
+
type ToggleVariants,
|
|
61
|
+
toggleVariants,
|
|
62
|
+
} from "./Toggle.js";
|
package/src/cli/add.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nebula add <component…>` — copy component sources into the project.
|
|
3
|
+
*
|
|
4
|
+
* The point of the registry model: what lands in the project is the source,
|
|
5
|
+
* and from then on it is the user's. No version, no upgrade path, no wrapper
|
|
6
|
+
* to fight when a design needs one class changed.
|
|
7
|
+
*
|
|
8
|
+
* Copies mirror the package's own layout, so `../lib/cn.js` resolves in the
|
|
9
|
+
* project exactly as it does here and no import is ever rewritten. Rewriting
|
|
10
|
+
* is where a copy-the-source CLI accumulates its edge cases; preserving the
|
|
11
|
+
* shape means there are none.
|
|
12
|
+
*
|
|
13
|
+
* Existing files are never overwritten without `--force`. The whole premise is
|
|
14
|
+
* that the user edits these, and a second `nebula add button` after a week of
|
|
15
|
+
* changes must not silently discard them.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
import { defaultPaths, type Language, type NebulaPaths } from "../config.js";
|
|
21
|
+
import { loadRegistry, packageRoot, resolveItems } from "./registry.js";
|
|
22
|
+
|
|
23
|
+
export interface AddOptions {
|
|
24
|
+
cwd: string;
|
|
25
|
+
names: readonly string[];
|
|
26
|
+
paths?: Partial<NebulaPaths>;
|
|
27
|
+
/** Overrides both the detected language and the configured one. */
|
|
28
|
+
language?: Language;
|
|
29
|
+
force?: boolean;
|
|
30
|
+
/** Print what would happen and write nothing. */
|
|
31
|
+
dryRun?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AddResult {
|
|
35
|
+
written: string[];
|
|
36
|
+
skipped: string[];
|
|
37
|
+
/** What was actually copied, so the caller can report it. */
|
|
38
|
+
language: Language;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Which language the project's component tree is written in.
|
|
43
|
+
*
|
|
44
|
+
* Read from what is already there rather than configured, because the answer
|
|
45
|
+
* is sitting on disk and a mismatched setting produces files the project
|
|
46
|
+
* cannot load. A tree holding `.ts` gets TypeScript; anything else — including
|
|
47
|
+
* a tree of `.js`, which is what an unbuilt Aurora app has — gets JavaScript.
|
|
48
|
+
* `undefined` when the directory does not exist yet.
|
|
49
|
+
*/
|
|
50
|
+
export function detectLanguage(componentsRoot: string): Language | undefined {
|
|
51
|
+
if (!existsSync(componentsRoot)) return undefined;
|
|
52
|
+
|
|
53
|
+
const stack = [componentsRoot];
|
|
54
|
+
let sawJs = false;
|
|
55
|
+
while (stack.length > 0) {
|
|
56
|
+
const dir = stack.pop();
|
|
57
|
+
if (dir === undefined) continue;
|
|
58
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
59
|
+
if (entry.name === "node_modules") continue;
|
|
60
|
+
const full = join(dir, entry.name);
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
stack.push(full);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (entry.name.endsWith(".d.ts")) continue;
|
|
66
|
+
if (entry.name.endsWith(".ts")) return "ts";
|
|
67
|
+
if (entry.name.endsWith(".js")) sawJs = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return sawJs ? "js" : undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function add(options: AddOptions): AddResult {
|
|
74
|
+
const root = packageRoot(join(options.cwd, "package.json"));
|
|
75
|
+
const registry = loadRegistry(join(root, "registry.json"));
|
|
76
|
+
const items = resolveItems(registry, options.names);
|
|
77
|
+
|
|
78
|
+
const target = join(
|
|
79
|
+
options.cwd,
|
|
80
|
+
options.paths?.components ?? defaultPaths.components,
|
|
81
|
+
);
|
|
82
|
+
const language = options.language ?? detectLanguage(target) ?? "js";
|
|
83
|
+
|
|
84
|
+
// The compiled output is what a browser can load. It ships in the package
|
|
85
|
+
// and `prepublishOnly` rebuilds it, so an installed copy always has it;
|
|
86
|
+
// inside this workspace it needs `pnpm build` first.
|
|
87
|
+
const sourceRoot = join(root, language === "ts" ? "src" : "dist");
|
|
88
|
+
if (!existsSync(sourceRoot)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`${sourceRoot} is missing — run \`pnpm build\` in the nebula package before adding in ${language} mode.`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const written: string[] = [];
|
|
95
|
+
const skipped: string[] = [];
|
|
96
|
+
// Shared files — `lib/cn.ts`, the primitives — belong to several items, so
|
|
97
|
+
// one run reaches the same path repeatedly. Without this, the second visit
|
|
98
|
+
// finds the file the first visit just wrote and reports it as pre-existing,
|
|
99
|
+
// which reads as a warning about the user's own work.
|
|
100
|
+
const handled = new Set<string>();
|
|
101
|
+
|
|
102
|
+
for (const item of items) {
|
|
103
|
+
for (const file of item.files) {
|
|
104
|
+
if (handled.has(file)) continue;
|
|
105
|
+
handled.add(file);
|
|
106
|
+
|
|
107
|
+
// The registry lists `.ts` paths; the compiled tree mirrors it exactly,
|
|
108
|
+
// so only the extension changes.
|
|
109
|
+
const relative = language === "ts" ? file : file.replace(/\.ts$/, ".js");
|
|
110
|
+
const from = join(sourceRoot, relative);
|
|
111
|
+
const to = join(target, relative);
|
|
112
|
+
|
|
113
|
+
if (existsSync(to) && options.force !== true) {
|
|
114
|
+
skipped.push(relative);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
written.push(relative);
|
|
118
|
+
if (options.dryRun === true) continue;
|
|
119
|
+
|
|
120
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
121
|
+
copyFileSync(from, to);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { written, skipped, language };
|
|
126
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The programmatic surface behind `ream nebula:add`.
|
|
3
|
+
*
|
|
4
|
+
* The Rust CLI cannot read `registry.json` or resolve the package's own
|
|
5
|
+
* install path, so — exactly as `nova.rs` does for VAPID keys — it runs Node
|
|
6
|
+
* against this entry point and lets the package do its own work. Everything
|
|
7
|
+
* that knows about the registry stays here, in the package that owns it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
type AddOptions,
|
|
12
|
+
type AddResult,
|
|
13
|
+
add,
|
|
14
|
+
detectLanguage,
|
|
15
|
+
} from "./add.js";
|
|
16
|
+
export {
|
|
17
|
+
loadRegistry,
|
|
18
|
+
packageRoot,
|
|
19
|
+
type Registry,
|
|
20
|
+
type RegistryItem,
|
|
21
|
+
resolveItems,
|
|
22
|
+
} from "./registry.js";
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `registry.json`.
|
|
3
|
+
*
|
|
4
|
+
* The file is generated (`pnpm registry`) and shipped with the package, so it
|
|
5
|
+
* is data arriving from disk — parsed, then checked before use. A malformed
|
|
6
|
+
* registry should fail with a sentence saying so, not with an undefined
|
|
7
|
+
* property three functions later.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { createRequire } from "node:module";
|
|
12
|
+
|
|
13
|
+
export type Layer = "atoms" | "molecules" | "organisms" | "templates";
|
|
14
|
+
|
|
15
|
+
export interface RegistryItem {
|
|
16
|
+
name: string;
|
|
17
|
+
title: string;
|
|
18
|
+
layer: Layer;
|
|
19
|
+
files: readonly string[];
|
|
20
|
+
dependencies: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Registry {
|
|
24
|
+
name: string;
|
|
25
|
+
items: readonly RegistryItem[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isLayer(value: unknown): value is Layer {
|
|
29
|
+
return (
|
|
30
|
+
value === "atoms" ||
|
|
31
|
+
value === "molecules" ||
|
|
32
|
+
value === "organisms" ||
|
|
33
|
+
value === "templates"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isStringArray(value: unknown): value is string[] {
|
|
38
|
+
return (
|
|
39
|
+
Array.isArray(value) && value.every((entry) => typeof entry === "string")
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isItem(value: unknown): value is RegistryItem {
|
|
44
|
+
if (typeof value !== "object" || value === null) return false;
|
|
45
|
+
const name = Reflect.get(value, "name");
|
|
46
|
+
const title = Reflect.get(value, "title");
|
|
47
|
+
const layer = Reflect.get(value, "layer");
|
|
48
|
+
const files = Reflect.get(value, "files");
|
|
49
|
+
const dependencies = Reflect.get(value, "dependencies");
|
|
50
|
+
return (
|
|
51
|
+
typeof name === "string" &&
|
|
52
|
+
typeof title === "string" &&
|
|
53
|
+
isLayer(layer) &&
|
|
54
|
+
isStringArray(files) &&
|
|
55
|
+
isStringArray(dependencies)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isRegistry(value: unknown): value is Registry {
|
|
60
|
+
if (typeof value !== "object" || value === null) return false;
|
|
61
|
+
const items = Reflect.get(value, "items");
|
|
62
|
+
return Array.isArray(items) && items.every(isItem);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Load the registry shipped alongside this package. */
|
|
66
|
+
export function loadRegistry(path: string): Registry {
|
|
67
|
+
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
68
|
+
if (!isRegistry(parsed)) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`${path} is not a valid nebula registry — regenerate it with \`pnpm registry\`.`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Where this package is installed, so its sources and registry can be read. */
|
|
77
|
+
export function packageRoot(from: string): string {
|
|
78
|
+
const require = createRequire(from);
|
|
79
|
+
// Resolve through the manifest rather than the entry point: the entry is
|
|
80
|
+
// `src/index.ts` in this workspace and `dist/index.js` once published, and
|
|
81
|
+
// only `package.json` is at a fixed place in both.
|
|
82
|
+
return require
|
|
83
|
+
.resolve("@c9up/nebula/package.json")
|
|
84
|
+
.replace(/package\.json$/, "");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* An item plus everything it needs, in install order.
|
|
89
|
+
*
|
|
90
|
+
* Depth-first with a seen set: Combobox pulls Command, Command pulls nothing
|
|
91
|
+
* further, and a diamond (two organisms sharing a molecule) copies it once.
|
|
92
|
+
*/
|
|
93
|
+
export function resolveItems(
|
|
94
|
+
registry: Registry,
|
|
95
|
+
names: readonly string[],
|
|
96
|
+
): RegistryItem[] {
|
|
97
|
+
const byName = new Map(registry.items.map((item) => [item.name, item]));
|
|
98
|
+
const seen = new Set<string>();
|
|
99
|
+
const ordered: RegistryItem[] = [];
|
|
100
|
+
|
|
101
|
+
function visit(name: string): void {
|
|
102
|
+
if (seen.has(name)) return;
|
|
103
|
+
seen.add(name);
|
|
104
|
+
const item = byName.get(name);
|
|
105
|
+
if (item === undefined) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`unknown component "${name}" — run \`ream nebula:list\` to see what exists.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
for (const dependency of item.dependencies) visit(dependency);
|
|
111
|
+
ordered.push(item);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const name of names) visit(name);
|
|
115
|
+
return ordered;
|
|
116
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nebula's configuration — `config/nebula.ts` in a ream app.
|
|
3
|
+
*
|
|
4
|
+
* nebula declares **no CSS dependency at all**, not even a peer one. The app
|
|
5
|
+
* installs whichever engine it wants and names it here; nebula generates the
|
|
6
|
+
* stubs and the build command that match. That mirrors how AdonisJS treats its
|
|
7
|
+
* asset bundler, and it is the reason `tailwindcss` does not appear in this
|
|
8
|
+
* package's `package.json`.
|
|
9
|
+
*
|
|
10
|
+
* // config/nebula.ts
|
|
11
|
+
* import { defineConfig } from '@c9up/nebula'
|
|
12
|
+
*
|
|
13
|
+
* export default defineConfig({
|
|
14
|
+
* adapter: 'tailwind',
|
|
15
|
+
* paths: {
|
|
16
|
+
* components: 'resources/pages',
|
|
17
|
+
* css: 'resources/css/app.css',
|
|
18
|
+
* output: 'public/app.css',
|
|
19
|
+
* },
|
|
20
|
+
* })
|
|
21
|
+
*
|
|
22
|
+
* The paths are also what the registry copies into: `ream nebula:add button`
|
|
23
|
+
* writes to `<components>/atoms/Button.ts`, because the atomic layer is a
|
|
24
|
+
* property of the component, not a choice made at install time.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export type AdapterName = "tailwind" | "unocss" | "css";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* What `nebula add` writes into the project.
|
|
31
|
+
*
|
|
32
|
+
* `"js"` copies the compiled output — valid ESM with `.js` specifiers and every
|
|
33
|
+
* doc comment intact, which a browser loads directly. That is the default,
|
|
34
|
+
* because an Aurora app serves `resources/pages` to the browser unbuilt; that
|
|
35
|
+
* zero-build-step promise is the framework's premise, and TypeScript sources
|
|
36
|
+
* dropped into that tree simply do not run.
|
|
37
|
+
*
|
|
38
|
+
* `"ts"` copies the TypeScript sources, for a project that compiles its front
|
|
39
|
+
* end. `nebula add` infers it from what the components directory already holds
|
|
40
|
+
* and only falls back to this setting when the directory is empty.
|
|
41
|
+
*/
|
|
42
|
+
export type Language = "ts" | "js";
|
|
43
|
+
|
|
44
|
+
export interface NebulaPaths {
|
|
45
|
+
/** Root of the atomic component tree. Layer directories live under it. */
|
|
46
|
+
components: string;
|
|
47
|
+
/** The stylesheet the adapter writes and the build compiles. */
|
|
48
|
+
css: string;
|
|
49
|
+
/** Where the compiled stylesheet lands. */
|
|
50
|
+
output: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface NebulaConfig {
|
|
54
|
+
/**
|
|
55
|
+
* Which CSS engine the app uses.
|
|
56
|
+
*
|
|
57
|
+
* `tailwind` and `unocss` consume the same class names, so switching
|
|
58
|
+
* between them changes the config and nothing else. `css` uses no engine:
|
|
59
|
+
* the build emits a stylesheet holding exactly the utilities nebula's
|
|
60
|
+
* components reference, and nothing has to be installed.
|
|
61
|
+
*/
|
|
62
|
+
adapter: AdapterName;
|
|
63
|
+
/** Overridden per-run by `--ts` / `--js`, and by what the tree already holds. */
|
|
64
|
+
language?: Language;
|
|
65
|
+
paths?: Partial<NebulaPaths>;
|
|
66
|
+
/**
|
|
67
|
+
* Override design tokens. Anything omitted keeps nebula's default, so a
|
|
68
|
+
* project can retune `--primary` without restating the palette.
|
|
69
|
+
*/
|
|
70
|
+
tokens?: Readonly<Record<string, string>>;
|
|
71
|
+
/** Same, for `.dark`. */
|
|
72
|
+
darkTokens?: Readonly<Record<string, string>>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ResolvedNebulaConfig extends NebulaConfig {
|
|
76
|
+
paths: NebulaPaths;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const defaultPaths: NebulaPaths = {
|
|
80
|
+
components: "resources/pages",
|
|
81
|
+
css: "resources/css/app.css",
|
|
82
|
+
output: "public/app.css",
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/** Type-checked config helper. Import it in `config/nebula.ts`. */
|
|
86
|
+
export function defineConfig(config: NebulaConfig): NebulaConfig {
|
|
87
|
+
return config;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Fill in the defaults. Used by the CLI and by the adapters. */
|
|
91
|
+
export function resolveConfig(config: NebulaConfig): ResolvedNebulaConfig {
|
|
92
|
+
return { ...config, paths: { ...defaultPaths, ...config.paths } };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The atomic layers, in dependency order. Also the registry's layer names. */
|
|
96
|
+
export const layers = ["atoms", "molecules", "organisms", "templates"] as const;
|
|
97
|
+
|
|
98
|
+
export type Layer = (typeof layers)[number];
|
|
99
|
+
|
|
100
|
+
export function isLayer(value: string): value is Layer {
|
|
101
|
+
return layers.some((layer) => layer === value);
|
|
102
|
+
}
|