@aggc/ui 0.4.0 → 0.5.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.
Files changed (158) hide show
  1. package/README.md +19 -0
  2. package/dist/chunks/UiSkeleton.vue_vue_type_script_setup_true_lang-Lghyrtms.js +1201 -0
  3. package/dist/chunks/pageHeader-CcJrPX_8.js +522 -0
  4. package/dist/components/PageSurface.styles.d.ts +1 -0
  5. package/dist/components/PageSurface.vue.d.ts +13 -0
  6. package/dist/components/ResultPanel.styles.d.ts +64 -0
  7. package/dist/components/ResultPanel.vue.d.ts +16 -0
  8. package/dist/components/SectionCard.styles.d.ts +13 -0
  9. package/dist/components/SectionCard.vue.d.ts +27 -0
  10. package/dist/components/StatusBadge.styles.d.ts +24 -0
  11. package/dist/components/StatusBadge.vue.d.ts +18 -0
  12. package/dist/components/UiButton.styles.d.ts +54 -0
  13. package/dist/components/UiButton.vue.d.ts +28 -0
  14. package/dist/components/UiCheckbox.styles.d.ts +42 -0
  15. package/dist/components/UiCheckbox.vue.d.ts +37 -0
  16. package/dist/components/UiField.styles.d.ts +7 -0
  17. package/dist/components/UiField.vue.d.ts +22 -0
  18. package/dist/components/UiLoadingState.styles.d.ts +4 -0
  19. package/dist/components/UiLoadingState.vue.d.ts +10 -0
  20. package/dist/components/UiSegmentedControl.styles.d.ts +23 -0
  21. package/dist/components/UiSegmentedControl.vue.d.ts +14 -0
  22. package/dist/components/UiSelect.styles.d.ts +104 -0
  23. package/dist/components/UiSelect.vue.d.ts +35 -0
  24. package/dist/components/UiSkeleton.styles.d.ts +67 -0
  25. package/dist/components/UiSkeleton.vue.d.ts +12 -0
  26. package/dist/components/index.d.ts +11 -0
  27. package/dist/components.d.ts +1 -0
  28. package/dist/components.js +14 -0
  29. package/dist/css.d.ts +2 -0
  30. package/dist/css.js +1 -0
  31. package/dist/fonts.css +6 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +65 -0
  34. package/dist/styles/index.d.ts +20 -0
  35. package/dist/styles/layouts/cluster.d.ts +27 -0
  36. package/dist/styles/layouts/page.d.ts +2 -0
  37. package/dist/styles/layouts/split.d.ts +22 -0
  38. package/dist/styles/layouts/stack.d.ts +19 -0
  39. package/dist/styles/patterns/actionToolbar.d.ts +1 -0
  40. package/dist/styles/patterns/emptyState.d.ts +2 -0
  41. package/dist/styles/patterns/infoPanel.d.ts +2 -0
  42. package/dist/styles/patterns/metricGrid.d.ts +22 -0
  43. package/dist/styles/patterns/pageHeader.d.ts +3 -0
  44. package/dist/styles/patterns/resultRegion.d.ts +1 -0
  45. package/dist/styles/patterns/selectableListDetail.d.ts +3 -0
  46. package/dist/styles/primitives/feedback.d.ts +4 -0
  47. package/dist/styles/primitives/fields.d.ts +3 -0
  48. package/dist/styles/primitives/surfaces.d.ts +3 -0
  49. package/dist/styles/primitives/typography.d.ts +6 -0
  50. package/dist/styles/recipes/badge.recipe.d.ts +32 -0
  51. package/dist/styles/recipes/button.recipe.d.ts +51 -0
  52. package/dist/styles/recipes/card.recipe.d.ts +59 -0
  53. package/dist/styles/recipes/dropdown.recipe.d.ts +25 -0
  54. package/dist/styles/recipes/input.recipe.d.ts +32 -0
  55. package/dist/styles.d.ts +1 -0
  56. package/dist/styles.js +429 -0
  57. package/dist/tokens/colors.d.ts +360 -0
  58. package/dist/tokens/core-colors.d.ts +238 -0
  59. package/dist/tokens/desktop-colors.d.ts +132 -0
  60. package/dist/tokens/index.d.ts +7 -0
  61. package/dist/tokens/motion.d.ts +10 -0
  62. package/dist/tokens/radius.d.ts +5 -0
  63. package/dist/tokens/spacing.d.ts +8 -0
  64. package/dist/tokens/typography.d.ts +10 -0
  65. package/dist/tokens-core.d.ts +5 -0
  66. package/dist/tokens-core.js +69 -0
  67. package/dist/tokens-desktop.d.ts +1 -0
  68. package/dist/tokens-desktop.js +30 -0
  69. package/dist/tokens.d.ts +1 -0
  70. package/dist/tokens.js +16 -0
  71. package/dist/ui.css +2026 -0
  72. package/package.json +3 -2
  73. package/src/components/PageSurface.styles.ts +3 -0
  74. package/src/components/PageSurface.vue +9 -0
  75. package/src/components/ResultPanel.styles.ts +108 -0
  76. package/src/components/ResultPanel.test.ts +22 -0
  77. package/src/components/ResultPanel.vue +70 -0
  78. package/src/components/SectionCard.styles.ts +65 -0
  79. package/src/components/SectionCard.test.ts +22 -0
  80. package/src/components/SectionCard.vue +51 -0
  81. package/src/components/StatusBadge.styles.ts +49 -0
  82. package/src/components/StatusBadge.test.ts +18 -0
  83. package/src/components/StatusBadge.vue +18 -0
  84. package/src/components/UiButton.styles.ts +29 -0
  85. package/src/components/UiButton.test.ts +21 -0
  86. package/src/components/UiButton.vue +46 -0
  87. package/src/components/UiCheckbox.styles.ts +118 -0
  88. package/src/components/UiCheckbox.test.ts +18 -0
  89. package/src/components/UiCheckbox.vue +72 -0
  90. package/src/components/UiField.styles.ts +35 -0
  91. package/src/components/UiField.test.ts +22 -0
  92. package/src/components/UiField.vue +36 -0
  93. package/src/components/UiLoadingState.styles.ts +36 -0
  94. package/src/components/UiLoadingState.vue +34 -0
  95. package/src/components/UiSegmentedControl.styles.ts +49 -0
  96. package/src/components/UiSegmentedControl.vue +30 -0
  97. package/src/components/UiSelect.styles.ts +214 -0
  98. package/src/components/UiSelect.test.ts +49 -0
  99. package/src/components/UiSelect.vue +256 -0
  100. package/src/components/UiSkeleton.styles.ts +93 -0
  101. package/src/components/UiSkeleton.vue +48 -0
  102. package/src/components/index.ts +11 -0
  103. package/src/components.ts +1 -0
  104. package/src/css/base.css +62 -0
  105. package/src/css/fonts.css +6 -0
  106. package/src/css/index.css +2 -0
  107. package/src/css/storybook.css +15 -0
  108. package/src/env.d.ts +1 -0
  109. package/src/index.ts +3 -0
  110. package/src/stories/feedback/ResultPanel.stories.ts +76 -0
  111. package/src/stories/feedback/StatusBadge.stories.ts +50 -0
  112. package/src/stories/feedback/UiLoadingState.stories.ts +52 -0
  113. package/src/stories/feedback/UiSkeleton.stories.ts +85 -0
  114. package/src/stories/forms/UiCheckbox.stories.ts +104 -0
  115. package/src/stories/forms/UiField.stories.ts +87 -0
  116. package/src/stories/forms/UiSelect.stories.ts +134 -0
  117. package/src/stories/layout/PageSurface.stories.ts +53 -0
  118. package/src/stories/layout/SectionCard.stories.ts +85 -0
  119. package/src/stories/primitives/UiButton.stories.ts +145 -0
  120. package/src/stories/primitives/UiSegmentedControl.stories.ts +67 -0
  121. package/src/stories/support/StoryThemeFrame.vue +101 -0
  122. package/src/stories/support/sources.ts +374 -0
  123. package/src/stories/support/storyStyles.ts +150 -0
  124. package/src/styles/README.md +23 -0
  125. package/src/styles/index.ts +20 -0
  126. package/src/styles/layouts/cluster.ts +27 -0
  127. package/src/styles/layouts/page.ts +22 -0
  128. package/src/styles/layouts/split.ts +26 -0
  129. package/src/styles/layouts/stack.ts +21 -0
  130. package/src/styles/patterns/actionToolbar.ts +8 -0
  131. package/src/styles/patterns/emptyState.ts +23 -0
  132. package/src/styles/patterns/infoPanel.ts +22 -0
  133. package/src/styles/patterns/metricGrid.ts +19 -0
  134. package/src/styles/patterns/pageHeader.ts +19 -0
  135. package/src/styles/patterns/resultRegion.ts +7 -0
  136. package/src/styles/patterns/selectableListDetail.ts +21 -0
  137. package/src/styles/primitives/feedback.ts +23 -0
  138. package/src/styles/primitives/fields.ts +76 -0
  139. package/src/styles/primitives/surfaces.ts +52 -0
  140. package/src/styles/primitives/typography.ts +42 -0
  141. package/src/styles/recipes/badge.recipe.ts +54 -0
  142. package/src/styles/recipes/button.recipe.ts +115 -0
  143. package/src/styles/recipes/card.recipe.ts +64 -0
  144. package/src/styles/recipes/dropdown.recipe.ts +40 -0
  145. package/src/styles/recipes/input.recipe.ts +59 -0
  146. package/src/styles.ts +1 -0
  147. package/src/test/setup.ts +1 -0
  148. package/src/tokens/colors.ts +16 -0
  149. package/src/tokens/core-colors.ts +53 -0
  150. package/src/tokens/desktop-colors.ts +37 -0
  151. package/src/tokens/index.ts +8 -0
  152. package/src/tokens/motion.ts +6 -0
  153. package/src/tokens/radius.ts +3 -0
  154. package/src/tokens/spacing.ts +4 -0
  155. package/src/tokens/typography.ts +6 -0
  156. package/src/tokens-core.ts +5 -0
  157. package/src/tokens-desktop.ts +1 -0
  158. package/src/tokens.ts +1 -0
@@ -0,0 +1,256 @@
1
+ <script setup lang="ts">
2
+ import { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from "vue";
3
+ import { Check, ChevronDown } from "lucide-vue-next";
4
+ import {
5
+ uiSelectCheckClass,
6
+ uiSelectChevronClass,
7
+ uiSelectMenuClass,
8
+ uiSelectMenuClassExtra,
9
+ uiSelectOptionClass,
10
+ uiSelectOptionContentClass,
11
+ uiSelectOptionDescriptionClass,
12
+ uiSelectOptionLabelClass,
13
+ uiSelectRootClass,
14
+ uiSelectTriggerClass,
15
+ uiSelectValueTextClass,
16
+ uiSelectValueWrapClass,
17
+ } from "./UiSelect.styles";
18
+
19
+ type UiSelectOption = {
20
+ label: string;
21
+ value: string;
22
+ description?: string;
23
+ };
24
+
25
+ const props = withDefaults(
26
+ defineProps<{
27
+ modelValue: string;
28
+ options: UiSelectOption[];
29
+ id?: string;
30
+ name?: string;
31
+ placeholder?: string;
32
+ disabled?: boolean;
33
+ invalid?: boolean;
34
+ size?: "sm" | "md";
35
+ ariaLabel?: string;
36
+ ariaLabelledby?: string;
37
+ ariaDescribedby?: string;
38
+ }>(),
39
+ {
40
+ id: undefined,
41
+ name: undefined,
42
+ placeholder: "Select an option",
43
+ disabled: false,
44
+ invalid: false,
45
+ size: "md",
46
+ ariaLabel: undefined,
47
+ ariaLabelledby: undefined,
48
+ ariaDescribedby: undefined,
49
+ }
50
+ );
51
+
52
+ const emit = defineEmits<{
53
+ "update:modelValue": [string];
54
+ }>();
55
+
56
+ const rootRef = ref<HTMLElement | null>(null);
57
+ const triggerRef = ref<HTMLButtonElement | null>(null);
58
+ const optionRefs = ref<(HTMLButtonElement | null)[]>([]);
59
+ const open = ref(false);
60
+ const activeIndex = ref(-1);
61
+ const generatedId = useId();
62
+ const baseId = computed(() => props.id ?? `ui-select-${generatedId}`);
63
+ const listId = computed(() => `${baseId.value}-listbox`);
64
+
65
+ const selectedOption = computed(
66
+ () => props.options.find((option) => option.value === props.modelValue) ?? null
67
+ );
68
+
69
+ function setActiveIndex(index: number) {
70
+ if (!props.options.length) {
71
+ activeIndex.value = -1;
72
+ return;
73
+ }
74
+
75
+ const normalized = (index + props.options.length) % props.options.length;
76
+ activeIndex.value = normalized;
77
+ nextTick(() => {
78
+ optionRefs.value[normalized]?.focus();
79
+ });
80
+ }
81
+
82
+ function closeMenu() {
83
+ open.value = false;
84
+ activeIndex.value = -1;
85
+ }
86
+
87
+ async function openMenu() {
88
+ if (props.disabled || !props.options.length) {
89
+ return;
90
+ }
91
+
92
+ open.value = true;
93
+ const selectedIndex = props.options.findIndex((option) => option.value === props.modelValue);
94
+ await nextTick();
95
+ setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);
96
+ }
97
+
98
+ function toggleMenu() {
99
+ if (open.value) {
100
+ closeMenu();
101
+ return;
102
+ }
103
+ void openMenu();
104
+ }
105
+
106
+ function selectOption(value: string) {
107
+ emit("update:modelValue", value);
108
+ closeMenu();
109
+ nextTick(() => {
110
+ triggerRef.value?.focus();
111
+ });
112
+ }
113
+
114
+ function onTriggerKeydown(event: KeyboardEvent) {
115
+ if (props.disabled) {
116
+ return;
117
+ }
118
+
119
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
120
+ event.preventDefault();
121
+ void openMenu();
122
+ }
123
+ }
124
+
125
+ function onOptionKeydown(event: KeyboardEvent, index: number) {
126
+ switch (event.key) {
127
+ case "ArrowDown":
128
+ event.preventDefault();
129
+ setActiveIndex(index + 1);
130
+ break;
131
+ case "ArrowUp":
132
+ event.preventDefault();
133
+ setActiveIndex(index - 1);
134
+ break;
135
+ case "Home":
136
+ event.preventDefault();
137
+ setActiveIndex(0);
138
+ break;
139
+ case "End":
140
+ event.preventDefault();
141
+ setActiveIndex(props.options.length - 1);
142
+ break;
143
+ case "Escape":
144
+ event.preventDefault();
145
+ closeMenu();
146
+ triggerRef.value?.focus();
147
+ break;
148
+ case "Enter":
149
+ case " ":
150
+ event.preventDefault();
151
+ selectOption(props.options[index].value);
152
+ break;
153
+ }
154
+ }
155
+
156
+ function onPointerDown(event: MouseEvent) {
157
+ if (!open.value) {
158
+ return;
159
+ }
160
+
161
+ if (rootRef.value?.contains(event.target as Node)) {
162
+ return;
163
+ }
164
+
165
+ closeMenu();
166
+ }
167
+
168
+ watch(
169
+ () => props.options,
170
+ () => {
171
+ optionRefs.value = [];
172
+ if (!props.options.some((option) => option.value === props.modelValue)) {
173
+ closeMenu();
174
+ }
175
+ }
176
+ );
177
+
178
+ watch(
179
+ () => props.disabled,
180
+ (disabled) => {
181
+ if (disabled) {
182
+ closeMenu();
183
+ }
184
+ }
185
+ );
186
+
187
+ onMounted(() => {
188
+ document.addEventListener("pointerdown", onPointerDown);
189
+ });
190
+
191
+ onBeforeUnmount(() => {
192
+ document.removeEventListener("pointerdown", onPointerDown);
193
+ });
194
+ </script>
195
+
196
+ <template>
197
+ <div ref="rootRef" :class="uiSelectRootClass({ open })">
198
+ <button
199
+ ref="triggerRef"
200
+ type="button"
201
+ :id="baseId"
202
+ :name="name"
203
+ :disabled="disabled"
204
+ :class="uiSelectTriggerClass({ size, open, selected: Boolean(selectedOption), disabled })"
205
+ aria-haspopup="listbox"
206
+ :aria-expanded="open"
207
+ :aria-controls="listId"
208
+ :aria-label="ariaLabel"
209
+ :aria-labelledby="ariaLabelledby"
210
+ :aria-describedby="ariaDescribedby"
211
+ :aria-invalid="invalid || undefined"
212
+ @click="toggleMenu"
213
+ @keydown="onTriggerKeydown"
214
+ >
215
+ <span :class="uiSelectValueWrapClass">
216
+ <span :class="uiSelectValueTextClass">
217
+ {{ selectedOption?.label ?? placeholder }}
218
+ </span>
219
+ </span>
220
+ <ChevronDown :size="16" :class="uiSelectChevronClass({ open })" />
221
+ </button>
222
+
223
+ <div
224
+ v-if="open"
225
+ :id="listId"
226
+ :class="[uiSelectMenuClass, uiSelectMenuClassExtra]"
227
+ role="listbox"
228
+ :aria-activedescendant="activeIndex >= 0 ? `${baseId}-${activeIndex}` : undefined"
229
+ >
230
+ <button
231
+ v-for="(option, index) in options"
232
+ :id="`${baseId}-${index}`"
233
+ :key="option.value"
234
+ :ref="(node) => (optionRefs[index] = node as HTMLButtonElement | null)"
235
+ type="button"
236
+ role="option"
237
+ :aria-selected="option.value === modelValue"
238
+ :class="uiSelectOptionClass({ size, active: activeIndex === index || option.value === modelValue })"
239
+ @mouseenter="activeIndex = index"
240
+ @click="selectOption(option.value)"
241
+ @keydown="onOptionKeydown($event, index)"
242
+ >
243
+ <span :class="uiSelectOptionContentClass">
244
+ <span :class="uiSelectOptionLabelClass">{{ option.label }}</span>
245
+ <span
246
+ v-if="option.description && size === 'md'"
247
+ :class="uiSelectOptionDescriptionClass"
248
+ >
249
+ {{ option.description }}
250
+ </span>
251
+ </span>
252
+ <Check :size="15" :class="uiSelectCheckClass({ selected: option.value === modelValue })" />
253
+ </button>
254
+ </div>
255
+ </div>
256
+ </template>
@@ -0,0 +1,93 @@
1
+ import { cva } from "@styled/css";
2
+
3
+ export const skeletonClass = cva({
4
+ base: {
5
+ position: "relative",
6
+ overflow: "hidden",
7
+ _before: {
8
+ content: "\"\"",
9
+ position: "absolute",
10
+ inset: "0",
11
+ background:
12
+ "linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.62) 50%, rgba(255,255,255,0) 100%)",
13
+ transform: "translateX(-100%)",
14
+ animation: "aggc-shimmer 1.45s ease-in-out infinite",
15
+ },
16
+ _dark: {
17
+ _before: {
18
+ background:
19
+ "linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(186,204,236,0.22) 50%, rgba(255,255,255,0) 100%)",
20
+ },
21
+ },
22
+ },
23
+ variants: {
24
+ variant: {
25
+ text: {
26
+ width: "100%",
27
+ height: "0.875rem",
28
+ borderRadius: "md",
29
+ bg: "rgba(127, 146, 184, 0.14)",
30
+ _dark: {
31
+ bg: "rgba(210, 220, 240, 0.09)",
32
+ },
33
+ },
34
+ title: {
35
+ width: "100%",
36
+ height: "1.75rem",
37
+ borderRadius: "xl",
38
+ bg: "rgba(127, 146, 184, 0.14)",
39
+ _dark: {
40
+ bg: "rgba(210, 220, 240, 0.09)",
41
+ },
42
+ },
43
+ rect: {
44
+ width: "100%",
45
+ height: "5rem",
46
+ borderRadius: "xl",
47
+ bg: "rgba(127, 146, 184, 0.14)",
48
+ _dark: {
49
+ bg: "rgba(210, 220, 240, 0.09)",
50
+ },
51
+ },
52
+ pill: {
53
+ width: "5rem",
54
+ height: "1.75rem",
55
+ borderRadius: "999px",
56
+ bg: "rgba(127, 146, 184, 0.14)",
57
+ _dark: {
58
+ bg: "rgba(210, 220, 240, 0.09)",
59
+ },
60
+ },
61
+ circle: {
62
+ width: "2.75rem",
63
+ height: "2.75rem",
64
+ borderRadius: "999px",
65
+ bg: "rgba(127, 146, 184, 0.14)",
66
+ _dark: {
67
+ bg: "rgba(210, 220, 240, 0.09)",
68
+ },
69
+ },
70
+ stat: {
71
+ width: "100%",
72
+ height: "4.75rem",
73
+ borderRadius: "xl",
74
+ bg: "rgba(117, 138, 178, 0.16)",
75
+ _dark: {
76
+ bg: "rgba(210, 220, 240, 0.11)",
77
+ },
78
+ },
79
+ action: {
80
+ width: "100%",
81
+ height: "2.75rem",
82
+ borderRadius: "xl",
83
+ bg: "rgba(127, 146, 184, 0.14)",
84
+ _dark: {
85
+ bg: "rgba(210, 220, 240, 0.09)",
86
+ },
87
+ },
88
+ },
89
+ },
90
+ defaultVariants: {
91
+ variant: "rect",
92
+ },
93
+ });
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { skeletonClass } from "./UiSkeleton.styles";
4
+
5
+ const props = withDefaults(
6
+ defineProps<{
7
+ variant?: "text" | "title" | "rect" | "pill" | "circle" | "stat" | "action";
8
+ width?: string;
9
+ height?: string;
10
+ }>(),
11
+ {
12
+ variant: "rect",
13
+ width: undefined,
14
+ height: undefined,
15
+ }
16
+ );
17
+
18
+ const skeletonClassName = computed(() => skeletonClass({ variant: props.variant }));
19
+ const skeletonStyle = computed(() => ({
20
+ width:
21
+ props.width ??
22
+ (props.variant === "circle" ? "2.75rem" : props.variant === "pill" ? "5rem" : "100%"),
23
+ height:
24
+ props.height ??
25
+ (props.variant === "text"
26
+ ? "0.875rem"
27
+ : props.variant === "title"
28
+ ? "1.75rem"
29
+ : props.variant === "pill"
30
+ ? "1.75rem"
31
+ : props.variant === "circle"
32
+ ? "2.75rem"
33
+ : props.variant === "action"
34
+ ? "2.75rem"
35
+ : props.variant === "stat"
36
+ ? "4.75rem"
37
+ : "5rem"),
38
+ }));
39
+ </script>
40
+
41
+ <template>
42
+ <div
43
+ aria-hidden="true"
44
+ data-ui-skeleton
45
+ :class="skeletonClassName"
46
+ :style="skeletonStyle"
47
+ />
48
+ </template>
@@ -0,0 +1,11 @@
1
+ export { default as PageSurface } from "./PageSurface.vue";
2
+ export { default as ResultPanel } from "./ResultPanel.vue";
3
+ export { default as SectionCard } from "./SectionCard.vue";
4
+ export { default as StatusBadge } from "./StatusBadge.vue";
5
+ export { default as UiButton } from "./UiButton.vue";
6
+ export { default as UiCheckbox } from "./UiCheckbox.vue";
7
+ export { default as UiField } from "./UiField.vue";
8
+ export { default as UiLoadingState } from "./UiLoadingState.vue";
9
+ export { default as UiSegmentedControl } from "./UiSegmentedControl.vue";
10
+ export { default as UiSelect } from "./UiSelect.vue";
11
+ export { default as UiSkeleton } from "./UiSkeleton.vue";
@@ -0,0 +1 @@
1
+ export * from "./components/index";
@@ -0,0 +1,62 @@
1
+ html {
2
+ color-scheme: light;
3
+ }
4
+
5
+ [data-theme="dark"] {
6
+ color-scheme: dark;
7
+ }
8
+
9
+ body {
10
+ margin: 0;
11
+ }
12
+
13
+ .aggc-spin {
14
+ animation: aggc-spin 800ms linear infinite;
15
+ }
16
+
17
+ [data-ui-loading-pulse] {
18
+ animation: loadingPulse 1.2s ease-out infinite;
19
+ }
20
+
21
+ [data-ui-skeleton]::before {
22
+ will-change: transform;
23
+ }
24
+
25
+ @keyframes aggc-spin {
26
+ from { transform: rotate(0deg); }
27
+ to { transform: rotate(360deg); }
28
+ }
29
+
30
+ @keyframes loadingPulse {
31
+ 0% {
32
+ transform: scale(0.96);
33
+ box-shadow: 0 0 0 0 rgba(49, 94, 255, 0.35);
34
+ }
35
+ 70% {
36
+ transform: scale(1);
37
+ box-shadow: 0 0 0 10px rgba(49, 94, 255, 0);
38
+ }
39
+ 100% {
40
+ transform: scale(0.96);
41
+ box-shadow: 0 0 0 0 rgba(49, 94, 255, 0);
42
+ }
43
+ }
44
+
45
+ @keyframes aggc-shimmer {
46
+ 100% {
47
+ transform: translateX(100%);
48
+ }
49
+ }
50
+
51
+ @media (prefers-reduced-motion: reduce) {
52
+ .aggc-spin,
53
+ [data-ui-loading-pulse] {
54
+ animation: none !important;
55
+ }
56
+
57
+ [data-ui-skeleton]::before {
58
+ animation: none !important;
59
+ transform: translateX(0);
60
+ opacity: 0.45;
61
+ }
62
+ }
@@ -0,0 +1,6 @@
1
+ @import "@fontsource/ibm-plex-sans/400.css";
2
+ @import "@fontsource/ibm-plex-sans/500.css";
3
+ @import "@fontsource/ibm-plex-sans/600.css";
4
+ @import "@fontsource/ibm-plex-sans/700.css";
5
+ @import "@fontsource/space-grotesk/500.css";
6
+ @import "@fontsource/space-grotesk/700.css";
@@ -0,0 +1,2 @@
1
+ @import "./base.css";
2
+ @import "../../styled-system/styles.css";
@@ -0,0 +1,15 @@
1
+ html,
2
+ body,
3
+ #storybook-root {
4
+ min-height: 100%;
5
+ }
6
+
7
+ body {
8
+ background:
9
+ radial-gradient(circle at top, rgba(49, 94, 255, 0.16), transparent 34%),
10
+ #0a1018;
11
+ }
12
+
13
+ #storybook-root {
14
+ padding: 1.5rem;
15
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./components/index";
2
+ export * from "./styles/index";
3
+ export * from "./tokens/index";
@@ -0,0 +1,76 @@
1
+ import type { Meta, StoryObj } from "@storybook/vue3-vite";
2
+ import { ref } from "vue";
3
+ import { ResultPanel } from "../../components";
4
+ import StoryThemeFrame from "../support/StoryThemeFrame.vue";
5
+ import { storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass } from "../support/storyStyles";
6
+ import { resultPanelFailureSource, resultPanelSuccessSource } from "../support/sources";
7
+
8
+ const meta = {
9
+ title: "Feedback/ResultPanel",
10
+ id: "result-panel",
11
+ component: ResultPanel,
12
+ tags: ["autodocs"],
13
+ args: {
14
+ ok: true,
15
+ summary: "Package validation passed",
16
+ },
17
+ parameters: {
18
+ docs: {
19
+ description: {
20
+ component: "Structured summary panel for success, warning and failure results.",
21
+ },
22
+ },
23
+ },
24
+ } satisfies Meta<typeof ResultPanel>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Success: Story = {
30
+ parameters: {
31
+ docs: { source: { code: resultPanelSuccessSource } },
32
+ },
33
+ render: () => ({
34
+ components: { ResultPanel, StoryThemeFrame },
35
+ setup() {
36
+ const previewTheme = ref<"light" | "dark">("dark");
37
+ return { previewTheme, storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass };
38
+ },
39
+ template: `
40
+ <StoryThemeFrame v-model:theme="previewTheme" preview-id="success">
41
+ <div :class="[storySurfaceClass, storySurfaceWideClass]">
42
+ <p :class="storySectionLabelClass">Pass state</p>
43
+ <ResultPanel ok summary="Package validation passed" :messages="['Typecheck passed', 'Tests passed', 'Build passed']" />
44
+ <p :class="storyCaptionClass">Use success panels to summarize what completed, not just to show a generic green state.</p>
45
+ </div>
46
+ </StoryThemeFrame>
47
+ `,
48
+ }),
49
+ };
50
+
51
+ export const Failure: Story = {
52
+ parameters: {
53
+ docs: { source: { code: resultPanelFailureSource } },
54
+ },
55
+ render: () => ({
56
+ components: { ResultPanel, StoryThemeFrame },
57
+ setup() {
58
+ const previewTheme = ref<"light" | "dark">("dark");
59
+ return { previewTheme, storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass };
60
+ },
61
+ template: `
62
+ <StoryThemeFrame v-model:theme="previewTheme" preview-id="failure">
63
+ <div :class="[storySurfaceClass, storySurfaceWideClass]">
64
+ <p :class="storySectionLabelClass">Fail state</p>
65
+ <ResultPanel
66
+ :ok="false"
67
+ summary="Package validation failed"
68
+ :warnings="['One component needs contrast review']"
69
+ :errors="['Visual regression diff detected']"
70
+ />
71
+ <p :class="storyCaptionClass">Failure panels should distinguish warnings from errors so the next action is obvious.</p>
72
+ </div>
73
+ </StoryThemeFrame>
74
+ `,
75
+ }),
76
+ };
@@ -0,0 +1,50 @@
1
+ import type { Meta, StoryObj } from "@storybook/vue3-vite";
2
+ import { StatusBadge } from "../../components";
3
+ import StoryThemeFrame from "../support/StoryThemeFrame.vue";
4
+ import { ref } from "vue";
5
+ import { storyCaptionClass, storyRowClass, storySectionLabelClass, storySurfaceClass, storySurfaceCompactClass } from "../support/storyStyles";
6
+ import { statusBadgeTonesSource } from "../support/sources";
7
+
8
+ const meta = {
9
+ title: "Feedback/StatusBadge",
10
+ id: "status-badge",
11
+ component: StatusBadge,
12
+ tags: ["autodocs"],
13
+ parameters: {
14
+ docs: {
15
+ description: {
16
+ component: "Compact state label for neutral, informative, warning and success contexts.",
17
+ },
18
+ },
19
+ },
20
+ } satisfies Meta<typeof StatusBadge>;
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof meta>;
24
+
25
+ export const Tones: Story = {
26
+ parameters: {
27
+ docs: { source: { code: statusBadgeTonesSource } },
28
+ },
29
+ render: () => ({
30
+ components: { StatusBadge, StoryThemeFrame },
31
+ setup() {
32
+ const previewTheme = ref<"light" | "dark">("dark");
33
+ return { previewTheme, storyCaptionClass, storyRowClass, storySectionLabelClass, storySurfaceClass, storySurfaceCompactClass };
34
+ },
35
+ template: `
36
+ <StoryThemeFrame v-model:theme="previewTheme" preview-id="tones">
37
+ <div :class="[storySurfaceClass, storySurfaceCompactClass]">
38
+ <p :class="storySectionLabelClass">Semantic tones</p>
39
+ <div :class="storyRowClass">
40
+ <StatusBadge tone="success">Ready</StatusBadge>
41
+ <StatusBadge tone="warning">Needs review</StatusBadge>
42
+ <StatusBadge tone="info">Preview</StatusBadge>
43
+ <StatusBadge tone="neutral">Draft</StatusBadge>
44
+ </div>
45
+ <p :class="storyCaptionClass">Badges should stay short and categorical so they work in cards, lists and headers.</p>
46
+ </div>
47
+ </StoryThemeFrame>
48
+ `,
49
+ }),
50
+ };
@@ -0,0 +1,52 @@
1
+ import type { Meta, StoryObj } from "@storybook/vue3-vite";
2
+ import { ref } from "vue";
3
+ import { UiLoadingState } from "../../components";
4
+ import StoryThemeFrame from "../support/StoryThemeFrame.vue";
5
+ import { storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceCompactClass } from "../support/storyStyles";
6
+ import { uiLoadingStateSource } from "../support/sources";
7
+
8
+ const meta = {
9
+ title: "Feedback/UiLoadingState",
10
+ id: "ui-loading-state",
11
+ component: UiLoadingState,
12
+ tags: ["autodocs"],
13
+ args: {
14
+ title: "Preparing release artifacts",
15
+ description: "Generating CSS, declarations and export map.",
16
+ },
17
+ parameters: {
18
+ docs: {
19
+ description: {
20
+ component: "Shared loading treatment for async flows that need a title and supporting description.",
21
+ },
22
+ },
23
+ },
24
+ } satisfies Meta<typeof UiLoadingState>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Overview: Story = {
30
+ parameters: {
31
+ docs: { source: { code: uiLoadingStateSource } },
32
+ },
33
+ render: () => ({
34
+ components: { StoryThemeFrame, UiLoadingState },
35
+ setup() {
36
+ const previewTheme = ref<"light" | "dark">("dark");
37
+ return { previewTheme, storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceCompactClass };
38
+ },
39
+ template: `
40
+ <StoryThemeFrame v-model:theme="previewTheme" preview-id="overview">
41
+ <div :class="[storySurfaceClass, storySurfaceCompactClass]">
42
+ <p :class="storySectionLabelClass">Async feedback</p>
43
+ <UiLoadingState
44
+ title="Preparing release artifacts"
45
+ description="Generating CSS, declarations and export map."
46
+ />
47
+ <p :class="storyCaptionClass">Use when an async action needs more structure than a spinner alone.</p>
48
+ </div>
49
+ </StoryThemeFrame>
50
+ `,
51
+ }),
52
+ };