@sewadah/selaras 0.2.0 → 0.3.1

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 (176) hide show
  1. package/README.md +36 -7
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +31 -15
  4. package/dist/runtime/components/Autocomplete.vue +3 -1
  5. package/dist/runtime/components/Badge.d.vue.ts +1 -1
  6. package/dist/runtime/components/Badge.vue.d.ts +1 -1
  7. package/dist/runtime/components/Calendar.d.vue.ts +69 -0
  8. package/dist/runtime/components/Calendar.vue +163 -0
  9. package/dist/runtime/components/Calendar.vue.d.ts +69 -0
  10. package/dist/runtime/components/Callout.vue +12 -1
  11. package/dist/runtime/components/CodeGroup.vue +13 -3
  12. package/dist/runtime/components/CodeTree.d.vue.ts +2 -0
  13. package/dist/runtime/components/CodeTree.vue +52 -1
  14. package/dist/runtime/components/CodeTree.vue.d.ts +2 -0
  15. package/dist/runtime/components/ColorPicker.d.vue.ts +8 -0
  16. package/dist/runtime/components/ColorPicker.vue +66 -45
  17. package/dist/runtime/components/ColorPicker.vue.d.ts +8 -0
  18. package/dist/runtime/components/Column.js +1 -1
  19. package/dist/runtime/components/CommandPalette.d.vue.ts +3 -1
  20. package/dist/runtime/components/CommandPalette.vue +5 -0
  21. package/dist/runtime/components/CommandPalette.vue.d.ts +3 -1
  22. package/dist/runtime/components/ContentNavigation.d.vue.ts +8 -1
  23. package/dist/runtime/components/ContentNavigation.vue +9 -2
  24. package/dist/runtime/components/ContentNavigation.vue.d.ts +8 -1
  25. package/dist/runtime/components/ContentToc.vue +14 -7
  26. package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
  27. package/dist/runtime/components/ContextMenu.vue +16 -4
  28. package/dist/runtime/components/ContextMenu.vue.d.ts +8 -1
  29. package/dist/runtime/components/DatePicker.d.vue.ts +10 -3
  30. package/dist/runtime/components/DatePicker.vue +24 -16
  31. package/dist/runtime/components/DatePicker.vue.d.ts +10 -3
  32. package/dist/runtime/components/Dropdown.d.vue.ts +33 -6
  33. package/dist/runtime/components/Dropdown.vue +143 -11
  34. package/dist/runtime/components/Dropdown.vue.d.ts +33 -6
  35. package/dist/runtime/components/FileTree.d.vue.ts +2 -0
  36. package/dist/runtime/components/FileTree.vue +8 -4
  37. package/dist/runtime/components/FileTree.vue.d.ts +2 -0
  38. package/dist/runtime/components/FileUpload.vue +1 -1
  39. package/dist/runtime/components/FormField.d.vue.ts +4 -0
  40. package/dist/runtime/components/FormField.vue +19 -4
  41. package/dist/runtime/components/FormField.vue.d.ts +4 -0
  42. package/dist/runtime/components/Input.vue +3 -2
  43. package/dist/runtime/components/Kbd.vue +12 -2
  44. package/dist/runtime/components/NavigationMenu.d.vue.ts +10 -10
  45. package/dist/runtime/components/NavigationMenu.vue +13 -7
  46. package/dist/runtime/components/NavigationMenu.vue.d.ts +10 -10
  47. package/dist/runtime/components/NavigationMenuAccordionItem.vue +5 -1
  48. package/dist/runtime/components/NavigationMenuFlyoutList.vue +7 -4
  49. package/dist/runtime/components/PinInput.vue +1 -1
  50. package/dist/runtime/components/Popover.d.vue.ts +8 -2
  51. package/dist/runtime/components/Popover.vue +9 -4
  52. package/dist/runtime/components/Popover.vue.d.ts +8 -2
  53. package/dist/runtime/components/ProsePre.d.vue.ts +6 -2
  54. package/dist/runtime/components/ProsePre.vue +11 -4
  55. package/dist/runtime/components/ProsePre.vue.d.ts +6 -2
  56. package/dist/runtime/components/Select.vue +3 -1
  57. package/dist/runtime/components/SplitterPanel.d.vue.ts +2 -2
  58. package/dist/runtime/components/SplitterPanel.vue.d.ts +2 -2
  59. package/dist/runtime/components/Table.vue +95 -26
  60. package/dist/runtime/components/Tabs.d.vue.ts +44 -4
  61. package/dist/runtime/components/Tabs.vue +171 -18
  62. package/dist/runtime/components/Tabs.vue.d.ts +44 -4
  63. package/dist/runtime/components/Textarea.vue +3 -2
  64. package/dist/runtime/components/Timeline.d.vue.ts +19 -0
  65. package/dist/runtime/components/Timeline.vue +99 -0
  66. package/dist/runtime/components/Timeline.vue.d.ts +19 -0
  67. package/dist/runtime/components/Toast.d.vue.ts +10 -1
  68. package/dist/runtime/components/Toast.vue +94 -8
  69. package/dist/runtime/components/Toast.vue.d.ts +10 -1
  70. package/dist/runtime/components/Tooltip.d.vue.ts +8 -2
  71. package/dist/runtime/components/Tooltip.vue +9 -4
  72. package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
  73. package/dist/runtime/composables/use-form-field.d.ts +1 -0
  74. package/dist/runtime/composables/use-table.d.ts +3 -0
  75. package/dist/runtime/composables/use-table.js +10 -4
  76. package/dist/runtime/composables/use-toast.js +5 -1
  77. package/dist/runtime/default-colors.css +1 -1
  78. package/dist/runtime/directives/hotkey.d.ts +13 -0
  79. package/dist/runtime/directives/hotkey.js +107 -0
  80. package/dist/runtime/directives/index.d.ts +2 -0
  81. package/dist/runtime/directives/index.js +2 -0
  82. package/dist/runtime/directives/mask.d.ts +30 -0
  83. package/dist/runtime/directives/mask.js +165 -0
  84. package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +29 -25
  85. package/dist/runtime/internal/ComboboxSelectBase.vue +104 -20
  86. package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +29 -25
  87. package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +16 -11
  88. package/dist/runtime/internal/ComboboxSelectBody.vue +49 -10
  89. package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +16 -11
  90. package/dist/runtime/internal/DatePickerCalendarBody.vue +1 -1
  91. package/dist/runtime/internal/DatePickerRangeCalendarBody.vue +1 -1
  92. package/dist/runtime/internal/ShortcutHint.d.vue.ts +6 -0
  93. package/dist/runtime/internal/ShortcutHint.vue +13 -0
  94. package/dist/runtime/internal/ShortcutHint.vue.d.ts +6 -0
  95. package/dist/runtime/internal/ToastItemRenderer.d.vue.ts +9 -1
  96. package/dist/runtime/internal/ToastItemRenderer.vue +55 -8
  97. package/dist/runtime/internal/ToastItemRenderer.vue.d.ts +9 -1
  98. package/dist/runtime/internal/combobox-select.js +2 -1
  99. package/dist/runtime/internal/programmatic-services.d.ts +2 -0
  100. package/dist/runtime/internal/programmatic-services.js +8 -0
  101. package/dist/runtime/internal/toast-stack.d.ts +36 -0
  102. package/dist/runtime/internal/toast-stack.js +36 -0
  103. package/dist/runtime/structural.css +1 -1
  104. package/dist/runtime/table.d.ts +1 -1
  105. package/dist/runtime/theme/alert-dialog.js +1 -1
  106. package/dist/runtime/theme/avatar.js +7 -7
  107. package/dist/runtime/theme/badge.js +2 -2
  108. package/dist/runtime/theme/calendar.d.ts +86 -0
  109. package/dist/runtime/theme/calendar.js +19 -0
  110. package/dist/runtime/theme/callout.d.ts +0 -12
  111. package/dist/runtime/theme/callout.js +8 -8
  112. package/dist/runtime/theme/checkbox.js +1 -1
  113. package/dist/runtime/theme/code-tree.js +1 -1
  114. package/dist/runtime/theme/command-palette.js +1 -1
  115. package/dist/runtime/theme/content-navigation.d.ts +3 -0
  116. package/dist/runtime/theme/content-navigation.js +1 -0
  117. package/dist/runtime/theme/date-picker.js +1 -1
  118. package/dist/runtime/theme/drawer.js +1 -1
  119. package/dist/runtime/theme/dropdown.d.ts +21 -0
  120. package/dist/runtime/theme/dropdown.js +15 -2
  121. package/dist/runtime/theme/file-tree.js +1 -1
  122. package/dist/runtime/theme/file-upload.js +2 -2
  123. package/dist/runtime/theme/form-field.d.ts +18 -0
  124. package/dist/runtime/theme/form-field.js +6 -1
  125. package/dist/runtime/theme/icon.js +1 -1
  126. package/dist/runtime/theme/input-number.js +1 -1
  127. package/dist/runtime/theme/input.js +1 -1
  128. package/dist/runtime/theme/modal.js +1 -1
  129. package/dist/runtime/theme/pin-input.js +1 -1
  130. package/dist/runtime/theme/progress.js +2 -2
  131. package/dist/runtime/theme/prose.d.ts +6 -0
  132. package/dist/runtime/theme/prose.js +2 -0
  133. package/dist/runtime/theme/radio-group.js +4 -4
  134. package/dist/runtime/theme/rating.js +1 -1
  135. package/dist/runtime/theme/select.js +2 -2
  136. package/dist/runtime/theme/separator.js +1 -1
  137. package/dist/runtime/theme/slideover.js +1 -1
  138. package/dist/runtime/theme/slider.js +2 -2
  139. package/dist/runtime/theme/splitter-resize-handle.js +1 -1
  140. package/dist/runtime/theme/switch.js +1 -1
  141. package/dist/runtime/theme/table.d.ts +27 -0
  142. package/dist/runtime/theme/table.js +11 -2
  143. package/dist/runtime/theme/tabs.d.ts +87 -3
  144. package/dist/runtime/theme/tabs.js +46 -22
  145. package/dist/runtime/theme/textarea.js +1 -1
  146. package/dist/runtime/theme/timeline.d.ts +251 -0
  147. package/dist/runtime/theme/timeline.js +84 -0
  148. package/dist/runtime/theme/toast.d.ts +92 -0
  149. package/dist/runtime/theme/toast.js +29 -7
  150. package/dist/runtime/theme-config.d.ts +12 -1
  151. package/dist/runtime/types/index.d.ts +6 -1
  152. package/dist/runtime/utils/arrow.d.ts +21 -0
  153. package/dist/runtime/utils/arrow.js +10 -0
  154. package/dist/runtime/utils/color-registry.d.ts +3 -1
  155. package/dist/runtime/utils/color-registry.js +4 -0
  156. package/dist/runtime/utils/file-icons.d.ts +8 -0
  157. package/dist/runtime/utils/file-icons.js +133 -0
  158. package/dist/runtime/utils/functional-tokens.d.ts +1 -1
  159. package/dist/runtime/utils/functional-tokens.js +1 -1
  160. package/dist/runtime/utils/icons.d.ts +2 -0
  161. package/dist/runtime/utils/icons.js +2 -0
  162. package/dist/runtime/utils/messages.d.ts +6 -0
  163. package/dist/runtime/utils/messages.js +6 -0
  164. package/dist/runtime/utils/navigation-menu.d.ts +10 -0
  165. package/dist/runtime/utils/navigation-menu.js +3 -0
  166. package/dist/runtime/utils/overlay.d.ts +17 -0
  167. package/dist/runtime/utils/overlay.js +7 -0
  168. package/dist/runtime/utils/seed-colors.d.ts +2 -1
  169. package/dist/runtime/utils/seed-colors.js +24 -16
  170. package/dist/runtime/utils/select-contracts.d.ts +7 -1
  171. package/dist/runtime/utils/table-columns.js +1 -1
  172. package/dist/runtime/utils/table-contracts.d.ts +9 -1
  173. package/dist/runtime/utils/timeline-contracts.d.ts +42 -0
  174. package/dist/runtime/utils/timeline-contracts.js +0 -0
  175. package/dist/tokens/default-colors.tokens.json +100 -100
  176. package/package.json +21 -9
package/README.md CHANGED
@@ -96,6 +96,26 @@ Every component is now auto-imported and ready to use:
96
96
  </SButton>
97
97
  ```
98
98
 
99
+ ## Theming
100
+
101
+ Selaras is CSS-first for design values.
102
+
103
+ For normal theme changes, override the public `--selaras-*` CSS variables or
104
+ the Selaras foundation palettes in Tailwind `@theme`. You do not need
105
+ `nuxt.config.ts` to change built-in roles such as `primary`, `warning`,
106
+ `danger`, or `neutral`.
107
+
108
+ Use `selaras.theme.colors` in `nuxt.config.ts` when you must register a new
109
+ role name, such as `tertiary`, or when you intentionally want to author a
110
+ complete build-time recipe.
111
+
112
+ `defineColorFromSeed()` is an optional accessibility-oriented generator. The
113
+ supplied color remains the exact resting solid fill; Selaras derives readable
114
+ content, interaction states, text, borders, focus, and essential indicators
115
+ around it. Use `defineColor()` when you need to author every leaf yourself.
116
+
117
+ See the full [Theming guide](./docs/content/overview/theming.md).
118
+
99
119
  ## Contribution
100
120
 
101
121
  <details>
@@ -105,14 +125,14 @@ Every component is now auto-imported and ready to use:
105
125
  # Install dependencies
106
126
  npm install
107
127
 
108
- # Generate type stubs
109
- npm run dev:prepare
128
+ # Generate type stubs and prepare the docs site
129
+ npm run docs:prepare
110
130
 
111
- # Develop with the playground (component docs site)
112
- npm run dev
131
+ # Develop with the docs site (documentation + component showcase)
132
+ npm run docs:dev
113
133
 
114
- # Build the playground
115
- npm run dev:build
134
+ # Generate the static docs site
135
+ npm run docs:generate
116
136
 
117
137
  # Type-check
118
138
  npm run test:types
@@ -128,8 +148,17 @@ Every component is now auto-imported and ready to use:
128
148
  # Requires Bun for the temporary consumer's dependency installation
129
149
  npm run test:compat
130
150
 
131
- # Release new version
151
+ # Release new versions (selaras, docs layer, or both)
132
152
  npm run release
153
+ npm run release:selaras
154
+ npm run release:docs
155
+
156
+ # Each package has its own version stream, so one run can bump them at
157
+ # different levels - Selaras major alongside a docs patch, for example.
158
+ # The release workflow exposes per-package bump/version inputs for this.
159
+ # Locally, drive each half through its own env:
160
+ RELEASE_BUMP=major npm run release:selaras
161
+ RELEASE_BUMP=patch npm run release:docs
133
162
  ```
134
163
 
135
164
  </details>
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "selaras",
3
3
  "configKey": "selaras",
4
- "version": "0.2.0",
4
+ "version": "0.3.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -6,19 +6,19 @@ import { builtinColorNames, createColorRegistry, generateColorRoleCss } from '..
6
6
  const foundationPaletteByRole = {
7
7
  primary: "indigo",
8
8
  secondary: "plum",
9
- success: "emerald",
9
+ success: "green",
10
10
  info: "blue",
11
- warning: "amber",
11
+ warning: "yellow",
12
12
  danger: "red",
13
13
  neutral: "gray"
14
14
  };
15
15
  const fillShadesByRole = {
16
16
  primary: { light: [500, 600, 700], dark: [400, 300, 200] },
17
17
  secondary: { light: [600, 700, 800], dark: [400, 300, 200] },
18
- success: { light: [700, 800, 900], dark: [500, 400, 300] },
18
+ success: { light: [500, 600, 700], dark: [500, 400, 300] },
19
19
  info: { light: [600, 700, 800], dark: [500, 400, 300] },
20
- warning: { light: [700, 800, 900], dark: [500, 400, 300] },
21
- danger: { light: [600, 700, 800], dark: [500, 400, 300] },
20
+ warning: { light: [500, 600, 700], dark: [500, 400, 300] },
21
+ danger: { light: [500, 600, 700], dark: [500, 400, 300] },
22
22
  neutral: { light: [950, 900, 800], dark: [200, 100, 50] }
23
23
  };
24
24
  function createBuiltinColorRegistry(classPrefix) {
@@ -26,29 +26,35 @@ function createBuiltinColorRegistry(classPrefix) {
26
26
  const recipes = Object.fromEntries(builtinColorNames.map((role) => {
27
27
  const mode = (dark) => {
28
28
  const neutral = role === "neutral";
29
- const warning = role === "warning";
29
+ const brightIntent = role === "success" || role === "warning" || role === "danger";
30
30
  const modeName = dark ? "dark" : "light";
31
31
  const [fillShade, hoverShade, pressedShade] = fillShadesByRole[role][modeName];
32
- const textShade = warning || role === "success" ? 800 : role === "info" || role === "danger" ? 700 : fillShade;
32
+ const textShade = brightIntent ? 800 : role === "info" ? 700 : fillShade;
33
33
  const fill = foundation(role, fillShade);
34
- const hover = foundation(role, hoverShade);
35
- const pressed = foundation(role, pressedShade);
34
+ const hover = brightIntent ? `color-mix(in oklab, ${fill} ${!dark && role === "danger" ? 98 : 94}%, ${dark ? "white" : "black"})` : foundation(role, hoverShade);
35
+ const pressed = brightIntent ? `color-mix(in oklab, ${fill} ${!dark && role === "danger" ? 96 : 88}%, ${dark ? "white" : "black"})` : foundation(role, pressedShade);
36
36
  const text = foundation(role, neutral ? dark ? 50 : 950 : dark ? 300 : textShade);
37
- const subtle = neutral ? foundation(role, dark ? 900 : 100) : dark ? `color-mix(in oklab, ${fill} 24%, var(--selaras-resolved-surface-default))` : foundation(role, 100);
37
+ const subtle = neutral ? foundation(role, dark ? 900 : 100) : dark ? `color-mix(in oklab, ${fill} ${brightIntent ? 16 : 24}%, var(--selaras-resolved-surface-default))` : foundation(role, brightIntent ? 50 : 100);
38
+ const indicatorShade = dark ? 400 : role === "success" || role === "warning" ? 700 : role === "danger" ? 500 : fillShade;
39
+ const indicator = neutral ? foundation(role, 500) : foundation(role, indicatorShade);
38
40
  return {
39
41
  fill,
40
42
  fillHover: hover,
41
43
  fillPressed: pressed,
42
- onFill: foundation("neutral", dark ? 950 : 25),
44
+ // The vivid light-mode intent fills intentionally use white text.
45
+ // Dark mode restores the shared dark foreground on its lighter fills.
46
+ // Consumers can override onFill in their own recipe.
47
+ onFill: brightIntent && !dark ? "#ffffff" : foundation("neutral", dark ? 950 : 25),
48
+ indicator,
43
49
  subtle,
44
- subtleHover: dark ? `color-mix(in oklab, ${foundation(role, 500)} 32%, var(--selaras-resolved-surface-default))` : `color-mix(in oklab, ${subtle} 94%, ${text})`,
45
- subtlePressed: dark ? `color-mix(in oklab, ${foundation(role, 500)} 40%, var(--selaras-resolved-surface-default))` : `color-mix(in oklab, ${subtle} 90%, ${text})`,
50
+ subtleHover: brightIntent ? `color-mix(in oklab, ${subtle} 96%, ${text})` : dark ? `color-mix(in oklab, ${foundation(role, 500)} 32%, var(--selaras-resolved-surface-default))` : `color-mix(in oklab, ${subtle} 94%, ${text})`,
51
+ subtlePressed: brightIntent ? `color-mix(in oklab, ${subtle} 92%, ${text})` : dark ? `color-mix(in oklab, ${foundation(role, 500)} 40%, var(--selaras-resolved-surface-default))` : `color-mix(in oklab, ${subtle} 90%, ${text})`,
46
52
  onSubtle: text,
47
53
  text,
48
54
  textHover: foundation(role, neutral ? dark ? 100 : 900 : dark ? 200 : Math.min(textShade + 100, 950)),
49
55
  textPressed: foundation(role, neutral ? dark ? 200 : 800 : dark ? 100 : Math.min(textShade + 200, 950)),
50
- border: neutral ? foundation(role, 500) : fill,
51
- focus: text
56
+ border: indicator,
57
+ focus: brightIntent ? indicator : text
52
58
  };
53
59
  };
54
60
  return [role, { light: mode(false), dark: mode(true) }];
@@ -132,6 +138,16 @@ const module$1 = defineNuxtModule({
132
138
  from: resolver.resolve("./runtime/directives/ripple"),
133
139
  meta: { vueDirective: true }
134
140
  });
141
+ addImports({
142
+ name: "vMask",
143
+ from: resolver.resolve("./runtime/directives/mask"),
144
+ meta: { vueDirective: true }
145
+ });
146
+ addImports({
147
+ name: "vHotkey",
148
+ from: resolver.resolve("./runtime/directives/hotkey"),
149
+ meta: { vueDirective: true }
150
+ });
135
151
  addTemplate({
136
152
  filename: "selaras-class-prefix.mjs",
137
153
  getContents: () => `export const classPrefix = ${JSON.stringify(options.classPrefix ?? null)}
@@ -30,7 +30,9 @@ const props = defineProps({
30
30
  searchTerm: { type: String, required: false },
31
31
  resetSearchTermOnBlur: { type: Boolean, required: false },
32
32
  resetSearchTermOnSelect: { type: Boolean, required: false },
33
- arrow: { type: Boolean, required: false },
33
+ arrow: { type: [Boolean, Object], required: false },
34
+ positioning: { type: Object, required: false },
35
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: void 0 },
34
36
  adaptive: { type: Boolean, required: false },
35
37
  ui: { type: Object, required: false }
36
38
  });
@@ -8,7 +8,7 @@ export interface BadgeProps {
8
8
  label?: string;
9
9
  icon?: string;
10
10
  trailingIcon?: string;
11
- /** A status circle. Inline dots use the badge foreground; standalone dots use the role text color. Supply an accessible name when no label is visible. */
11
+ /** A status circle. Inline dots use the badge foreground; standalone dots use the role indicator. Supply an accessible name when no label is visible. */
12
12
  dot?: boolean;
13
13
  color?: ColorRole;
14
14
  variant?: BadgeVariants['variant'];
@@ -8,7 +8,7 @@ export interface BadgeProps {
8
8
  label?: string;
9
9
  icon?: string;
10
10
  trailingIcon?: string;
11
- /** A status circle. Inline dots use the badge foreground; standalone dots use the role text color. Supply an accessible name when no label is visible. */
11
+ /** A status circle. Inline dots use the badge foreground; standalone dots use the role indicator. Supply an accessible name when no label is visible. */
12
12
  dot?: boolean;
13
13
  color?: ColorRole;
14
14
  variant?: BadgeVariants['variant'];
@@ -0,0 +1,69 @@
1
+ import type { DateValue } from '@internationalized/date';
2
+ import type { CalendarThemeSlots } from '../theme/calendar.js';
3
+ import type { ColorRole } from '../utils/color-registry.js';
4
+ import type { UiProp } from '../utils/ui.js';
5
+ type __VLS_Slots = {
6
+ 'day'?: (props: CalendarDaySlotProps) => any;
7
+ /** Renders beside the date trigger within its grid cell. Safe for event links/buttons. */
8
+ 'day-details'?: (props: {
9
+ date: DateValue;
10
+ month: DateValue;
11
+ }) => any;
12
+ 'footer'?: () => any;
13
+ };
14
+ export interface CalendarProps {
15
+ modelValue?: DateValue;
16
+ defaultValue?: DateValue;
17
+ /** The displayed month when controlled independently of the selected date. */
18
+ placeholder?: DateValue;
19
+ defaultPlaceholder?: DateValue;
20
+ minValue?: DateValue;
21
+ maxValue?: DateValue;
22
+ isDateDisabled?: (date: DateValue) => boolean;
23
+ isDateUnavailable?: (date: DateValue) => boolean;
24
+ locale?: string;
25
+ numberOfMonths?: number;
26
+ pagedNavigation?: boolean;
27
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
28
+ weekdayFormat?: 'narrow' | 'short' | 'long';
29
+ fixedWeeks?: boolean;
30
+ disableDaysOutsideCurrentView?: boolean;
31
+ preventDeselect?: boolean;
32
+ disabled?: boolean;
33
+ readonly?: boolean;
34
+ initialFocus?: boolean;
35
+ calendarLabel?: string;
36
+ color?: ColorRole;
37
+ activeColor?: ColorRole;
38
+ ui?: UiProp<CalendarThemeSlots>;
39
+ }
40
+ export interface CalendarEmits {
41
+ 'update:modelValue': [value: DateValue | undefined];
42
+ 'update:placeholder': [value: DateValue];
43
+ }
44
+ export interface CalendarDaySlotProps {
45
+ date: DateValue;
46
+ month: DateValue;
47
+ dayValue: string;
48
+ selected: boolean;
49
+ today: boolean;
50
+ disabled: boolean;
51
+ unavailable: boolean;
52
+ outsideView: boolean;
53
+ outsideVisibleView: boolean;
54
+ }
55
+ declare const __VLS_base: import("vue").DefineComponent<CalendarProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
56
+ "update:modelValue": (value: DateValue | undefined) => any;
57
+ "update:placeholder": (value: DateValue) => any;
58
+ }, string, import("vue").PublicProps, Readonly<CalendarProps> & Readonly<{
59
+ "onUpdate:modelValue"?: ((value: DateValue | undefined) => any) | undefined;
60
+ "onUpdate:placeholder"?: ((value: DateValue) => any) | undefined;
61
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
62
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
63
+ declare const _default: typeof __VLS_export;
64
+ export default _default;
65
+ type __VLS_WithSlots<T, S> = T & {
66
+ new (): {
67
+ $slots: S;
68
+ };
69
+ };
@@ -0,0 +1,163 @@
1
+ <script setup>
2
+ import {
3
+ CalendarCell,
4
+ CalendarCellTrigger,
5
+ CalendarGrid,
6
+ CalendarGridBody,
7
+ CalendarGridHead,
8
+ CalendarGridRow,
9
+ CalendarHeadCell,
10
+ CalendarHeader,
11
+ CalendarHeading,
12
+ CalendarNext,
13
+ CalendarPrev,
14
+ CalendarRoot
15
+ } from "reka-ui";
16
+ import { computed, getCurrentInstance, useId, useSlots } from "vue";
17
+ import { useIcons } from "../composables/use-icons";
18
+ import { useMessages } from "../composables/use-messages";
19
+ import { calendarTheme } from "../theme/calendar";
20
+ import { resolveRegisteredColorRole } from "../utils/registered-colors";
21
+ import { resolveSlot, useComponentTheme, useRootProps } from "../utils/ui";
22
+ import Button from "./Button.vue";
23
+ defineOptions({ inheritAttrs: false });
24
+ const props = defineProps({
25
+ modelValue: { type: Object, required: false },
26
+ defaultValue: { type: Object, required: false },
27
+ placeholder: { type: Object, required: false },
28
+ defaultPlaceholder: { type: Object, required: false },
29
+ minValue: { type: Object, required: false },
30
+ maxValue: { type: Object, required: false },
31
+ isDateDisabled: { type: Function, required: false },
32
+ isDateUnavailable: { type: Function, required: false },
33
+ locale: { type: String, required: false },
34
+ numberOfMonths: { type: Number, required: false },
35
+ pagedNavigation: { type: Boolean, required: false },
36
+ weekStartsOn: { type: Number, required: false },
37
+ weekdayFormat: { type: String, required: false },
38
+ fixedWeeks: { type: Boolean, required: false },
39
+ disableDaysOutsideCurrentView: { type: Boolean, required: false },
40
+ preventDeselect: { type: Boolean, required: false },
41
+ disabled: { type: Boolean, required: false },
42
+ readonly: { type: Boolean, required: false },
43
+ initialFocus: { type: Boolean, required: false },
44
+ calendarLabel: { type: String, required: false },
45
+ color: { type: null, required: false, default: "neutral" },
46
+ activeColor: { type: null, required: false, default: "primary" },
47
+ ui: { type: Object, required: false }
48
+ });
49
+ const emit = defineEmits(["update:modelValue", "update:placeholder"]);
50
+ defineSlots();
51
+ const slots = useSlots();
52
+ const instance = getCurrentInstance();
53
+ const detailsId = useId();
54
+ const icons = useIcons();
55
+ const messages = useMessages();
56
+ const theme = useComponentTheme("calendar", calendarTheme);
57
+ const ui = computed(() => theme.value());
58
+ const color = computed(() => resolveRegisteredColorRole(props.color, "neutral"));
59
+ const activeColor = computed(() => resolveRegisteredColorRole(props.activeColor, "primary"));
60
+ const rootModelValue = computed(() => {
61
+ const vnodeProps = instance.vnode.props ?? {};
62
+ const isControlled = Object.hasOwn(vnodeProps, "modelValue") || Object.hasOwn(vnodeProps, "model-value");
63
+ return isControlled && props.modelValue === void 0 ? null : props.modelValue;
64
+ });
65
+ const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
66
+ const headerProps = computed(() => resolveSlot(ui.value.header, props.ui?.header));
67
+ const headingProps = computed(() => resolveSlot(ui.value.heading, props.ui?.heading));
68
+ const gridsProps = computed(() => resolveSlot(ui.value.grids, props.ui?.grids));
69
+ const gridProps = computed(() => resolveSlot(ui.value.grid, props.ui?.grid));
70
+ const gridHeadProps = computed(() => resolveSlot(ui.value.gridHead, props.ui?.gridHead));
71
+ const headCellProps = computed(() => resolveSlot(ui.value.headCell, props.ui?.headCell));
72
+ const cellProps = computed(() => resolveSlot(ui.value.cell, props.ui?.cell));
73
+ const dayButtonProps = computed(() => resolveSlot(ui.value.dayButton, props.ui?.dayButton));
74
+ const dayDetailsProps = computed(() => resolveSlot(ui.value.dayDetails, props.ui?.dayDetails));
75
+ const footerProps = computed(() => resolveSlot(ui.value.footer, props.ui?.footer));
76
+ const navButtonUi = { leadingIcon: "rtl:-scale-x-100" };
77
+ const dayDetailsId = (month, date) => `${detailsId}-${month.toString()}-${date.toString()}`;
78
+ </script>
79
+
80
+ <template>
81
+ <CalendarRoot
82
+ :model-value="rootModelValue"
83
+ :default-value="defaultValue"
84
+ :placeholder="placeholder"
85
+ :default-placeholder="defaultPlaceholder"
86
+ :min-value="minValue"
87
+ :max-value="maxValue"
88
+ :is-date-disabled="isDateDisabled"
89
+ :is-date-unavailable="isDateUnavailable"
90
+ :locale="locale"
91
+ :number-of-months="numberOfMonths"
92
+ :paged-navigation="pagedNavigation"
93
+ :week-starts-on="weekStartsOn"
94
+ :weekday-format="weekdayFormat"
95
+ :fixed-weeks="fixedWeeks"
96
+ :disable-days-outside-current-view="disableDaysOutsideCurrentView"
97
+ :prevent-deselect="preventDeselect"
98
+ :disabled="disabled"
99
+ :readonly="readonly"
100
+ :initial-focus="initialFocus"
101
+ :calendar-label="calendarLabel"
102
+ :data-selaras-color="activeColor"
103
+ v-bind="rootProps"
104
+ @update:model-value="(value) => emit('update:modelValue', value)"
105
+ @update:placeholder="(value) => emit('update:placeholder', value)"
106
+ >
107
+ <template #default="{ grid, weekDays }">
108
+ <CalendarHeader v-bind="headerProps">
109
+ <CalendarPrev as-child>
110
+ <Button variant="ghost" :color="color" size="sm" :icon="icons.chevronLeft" :aria-label="messages.previousMonth" :ui="navButtonUi" />
111
+ </CalendarPrev>
112
+ <CalendarHeading v-bind="headingProps" />
113
+ <CalendarNext as-child>
114
+ <Button variant="ghost" :color="color" size="sm" :icon="icons.chevronRight" :aria-label="messages.nextMonth" :ui="navButtonUi" />
115
+ </CalendarNext>
116
+ </CalendarHeader>
117
+
118
+ <div v-bind="gridsProps">
119
+ <CalendarGrid v-for="month in grid" :key="month.value.toString()" v-bind="gridProps">
120
+ <CalendarGridHead v-bind="gridHeadProps">
121
+ <CalendarGridRow>
122
+ <CalendarHeadCell v-for="(day, index) in weekDays" :key="index" v-bind="headCellProps">
123
+ {{ day }}
124
+ </CalendarHeadCell>
125
+ </CalendarGridRow>
126
+ </CalendarGridHead>
127
+ <CalendarGridBody>
128
+ <CalendarGridRow v-for="(week, weekIndex) in month.rows" :key="weekIndex">
129
+ <CalendarCell v-for="date in week" :key="date.toString()" :date="date" v-bind="cellProps">
130
+ <CalendarCellTrigger
131
+ v-slot="dayState"
132
+ :day="date"
133
+ :month="month.value"
134
+ as-child
135
+ >
136
+ <Button
137
+ :variant="dayState.selected ? 'solid' : 'ghost'"
138
+ :color="dayState.selected ? activeColor : color"
139
+ size="sm"
140
+ square
141
+ :disabled="dayState.disabled"
142
+ :aria-describedby="slots['day-details'] ? dayDetailsId(month.value, date) : void 0"
143
+ v-bind="dayButtonProps"
144
+ >
145
+ <slot name="day" :date="date" :month="month.value" v-bind="dayState">
146
+ {{ dayState.dayValue }}
147
+ </slot>
148
+ </Button>
149
+ </CalendarCellTrigger>
150
+ <div v-if="slots['day-details']" :id="dayDetailsId(month.value, date)" v-bind="dayDetailsProps">
151
+ <slot name="day-details" :date="date" :month="month.value" />
152
+ </div>
153
+ </CalendarCell>
154
+ </CalendarGridRow>
155
+ </CalendarGridBody>
156
+ </CalendarGrid>
157
+ </div>
158
+ <div v-if="slots.footer" v-bind="footerProps">
159
+ <slot name="footer" />
160
+ </div>
161
+ </template>
162
+ </CalendarRoot>
163
+ </template>
@@ -0,0 +1,69 @@
1
+ import type { DateValue } from '@internationalized/date';
2
+ import type { CalendarThemeSlots } from '../theme/calendar.js';
3
+ import type { ColorRole } from '../utils/color-registry.js';
4
+ import type { UiProp } from '../utils/ui.js';
5
+ type __VLS_Slots = {
6
+ 'day'?: (props: CalendarDaySlotProps) => any;
7
+ /** Renders beside the date trigger within its grid cell. Safe for event links/buttons. */
8
+ 'day-details'?: (props: {
9
+ date: DateValue;
10
+ month: DateValue;
11
+ }) => any;
12
+ 'footer'?: () => any;
13
+ };
14
+ export interface CalendarProps {
15
+ modelValue?: DateValue;
16
+ defaultValue?: DateValue;
17
+ /** The displayed month when controlled independently of the selected date. */
18
+ placeholder?: DateValue;
19
+ defaultPlaceholder?: DateValue;
20
+ minValue?: DateValue;
21
+ maxValue?: DateValue;
22
+ isDateDisabled?: (date: DateValue) => boolean;
23
+ isDateUnavailable?: (date: DateValue) => boolean;
24
+ locale?: string;
25
+ numberOfMonths?: number;
26
+ pagedNavigation?: boolean;
27
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
28
+ weekdayFormat?: 'narrow' | 'short' | 'long';
29
+ fixedWeeks?: boolean;
30
+ disableDaysOutsideCurrentView?: boolean;
31
+ preventDeselect?: boolean;
32
+ disabled?: boolean;
33
+ readonly?: boolean;
34
+ initialFocus?: boolean;
35
+ calendarLabel?: string;
36
+ color?: ColorRole;
37
+ activeColor?: ColorRole;
38
+ ui?: UiProp<CalendarThemeSlots>;
39
+ }
40
+ export interface CalendarEmits {
41
+ 'update:modelValue': [value: DateValue | undefined];
42
+ 'update:placeholder': [value: DateValue];
43
+ }
44
+ export interface CalendarDaySlotProps {
45
+ date: DateValue;
46
+ month: DateValue;
47
+ dayValue: string;
48
+ selected: boolean;
49
+ today: boolean;
50
+ disabled: boolean;
51
+ unavailable: boolean;
52
+ outsideView: boolean;
53
+ outsideVisibleView: boolean;
54
+ }
55
+ declare const __VLS_base: import("vue").DefineComponent<CalendarProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
56
+ "update:modelValue": (value: DateValue | undefined) => any;
57
+ "update:placeholder": (value: DateValue) => any;
58
+ }, string, import("vue").PublicProps, Readonly<CalendarProps> & Readonly<{
59
+ "onUpdate:modelValue"?: ((value: DateValue | undefined) => any) | undefined;
60
+ "onUpdate:placeholder"?: ((value: DateValue) => any) | undefined;
61
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
62
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
63
+ declare const _default: typeof __VLS_export;
64
+ export default _default;
65
+ type __VLS_WithSlots<T, S> = T & {
66
+ new (): {
67
+ $slots: S;
68
+ };
69
+ };
@@ -24,6 +24,13 @@ const iconName = computed(() => {
24
24
  return void 0;
25
25
  return props.icon ?? icons.value[DEFAULT_ICON[props.type ?? "note"]];
26
26
  });
27
+ const colorForType = {
28
+ note: "info",
29
+ tip: "success",
30
+ warning: "warning",
31
+ danger: "danger"
32
+ };
33
+ const effectiveColor = computed(() => colorForType[props.type ?? "note"]);
27
34
  const theme = useComponentTheme("callout", calloutTheme);
28
35
  const ui = computed(() => theme.value({ type: props.type }));
29
36
  const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
@@ -33,7 +40,7 @@ const titleProps = computed(() => resolveSlot(ui.value.title, props.ui?.title));
33
40
  </script>
34
41
 
35
42
  <template>
36
- <div v-bind="rootProps" role="note">
43
+ <div v-bind="rootProps" data-selaras-callout :data-selaras-color="effectiveColor" role="note">
37
44
  <Icon v-if="iconName" :name="iconName" v-bind="iconProps" />
38
45
  <div v-bind="contentProps">
39
46
  <p v-if="title || $slots.title" v-bind="titleProps">
@@ -45,3 +52,7 @@ const titleProps = computed(() => resolveSlot(ui.value.title, props.ui?.title));
45
52
  </div>
46
53
  </div>
47
54
  </template>
55
+
56
+ <style scoped>
57
+ [data-selaras-callout]:before{-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}
58
+ </style>
@@ -1,6 +1,8 @@
1
1
  <script setup>
2
2
  import { cloneVNode, Comment, computed, Fragment, ref, Text, useSlots } from "vue";
3
3
  import { useMessages } from "../composables/use-messages";
4
+ import { resolveFileIcon } from "../utils/file-icons";
5
+ import ProsePre from "./ProsePre.vue";
4
6
  import Tabs from "./Tabs.vue";
5
7
  const slots = useSlots();
6
8
  const messages = useMessages();
@@ -22,15 +24,23 @@ function labelFor(child, index) {
22
24
  const props = child.props ?? {};
23
25
  return props.filename || props.language || messages.value.codeTabFallback(index + 1);
24
26
  }
27
+ function iconFor(child) {
28
+ const props = child.props ?? {};
29
+ return props.icon ?? resolveFileIcon(props.language, props.filename);
30
+ }
31
+ function cloneForTab(child) {
32
+ return cloneVNode(child, child.type === ProsePre ? { showHeader: false } : void 0);
33
+ }
25
34
  const tabItems = computed(() => children.value.map((child, index) => ({
26
35
  label: labelFor(child, index),
27
- value: String(index)
36
+ value: String(index),
37
+ icon: iconFor(child)
28
38
  })));
29
39
  const activeTab = ref("0");
30
40
  </script>
31
41
 
32
42
  <template>
33
- <Tabs v-model="activeTab" :items="tabItems">
43
+ <Tabs v-model="activeTab" :items="tabItems" variant="pill">
34
44
  <template v-for="(child, index) in children" :key="index" #[String(index)]>
35
45
  <!--
36
46
  cloneVNode is required, not optional: this vnode was captured from
@@ -40,7 +50,7 @@ const activeTab = ref("0");
40
50
  hydration-shape mismatch (Fragment vs text) the first time this
41
51
  panel mounts. Cloning gives it a fresh identity for its new spot.
42
52
  -->
43
- <component :is="() => cloneVNode(child)" />
53
+ <component :is="() => cloneForTab(child)" />
44
54
  </template>
45
55
  </Tabs>
46
56
  </template>
@@ -4,6 +4,8 @@ import type { FileTreeNode } from './FileTree.vue.js';
4
4
  export interface CodeTreeFile extends FileTreeNode {
5
5
  /** The file's content - read for a leaf node once selected, ignored for a directory. */
6
6
  code?: string;
7
+ /** Overrides the language inferred from the filename. */
8
+ language?: string;
7
9
  children?: CodeTreeFile[];
8
10
  }
9
11
  export interface CodeTreeProps {
@@ -21,6 +21,56 @@ function findFirstFile(nodes) {
21
21
  return void 0;
22
22
  }
23
23
  const selected = ref(findFirstFile(props.items));
24
+ const highlightedCode = ref("");
25
+ const languageByExtension = {
26
+ bash: "bash",
27
+ cjs: "javascript",
28
+ css: "css",
29
+ htm: "html",
30
+ html: "html",
31
+ js: "javascript",
32
+ jsx: "jsx",
33
+ json: "json",
34
+ jsonc: "jsonc",
35
+ md: "markdown",
36
+ mjs: "javascript",
37
+ mts: "typescript",
38
+ sh: "bash",
39
+ ts: "typescript",
40
+ tsx: "tsx",
41
+ vue: "vue",
42
+ yaml: "yaml",
43
+ yml: "yaml"
44
+ };
45
+ function languageFor(file) {
46
+ if (file.language)
47
+ return file.language;
48
+ const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
49
+ return languageByExtension[extension] ?? "text";
50
+ }
51
+ watch(selected, async (file, _previous, onCleanup) => {
52
+ let stale = false;
53
+ onCleanup(() => {
54
+ stale = true;
55
+ });
56
+ highlightedCode.value = "";
57
+ if (!file?.code)
58
+ return;
59
+ try {
60
+ const { codeToHtml } = await import("shiki");
61
+ const html = await codeToHtml(file.code, {
62
+ lang: languageFor(file),
63
+ themes: { light: "github-light", dark: "github-dark" },
64
+ defaultColor: false
65
+ });
66
+ if (stale)
67
+ return;
68
+ highlightedCode.value = html.match(/<code(?:\s[^>]*)?>([\s\S]*?)<\/code>/)?.[1] ?? "";
69
+ } catch {
70
+ if (!stale)
71
+ highlightedCode.value = "";
72
+ }
73
+ }, { immediate: true });
24
74
  watch(() => props.items, () => {
25
75
  selected.value = findFirstFile(props.items);
26
76
  });
@@ -28,6 +78,7 @@ const messages = useMessages();
28
78
  const theme = useComponentTheme("codeTree", codeTreeTheme);
29
79
  const ui = computed(() => theme.value());
30
80
  const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
81
+ const codeMode = computed(() => rootProps.value["data-selaras-mode"]);
31
82
  const treeProps = computed(() => resolveSlot(ui.value.tree, props.ui?.tree));
32
83
  const contentProps = computed(() => resolveSlot(ui.value.content, props.ui?.content));
33
84
  const preProps = computed(() => resolveSlot(ui.value.pre, props.ui?.pre));
@@ -47,7 +98,7 @@ const fileTreeUi = { root: "rounded-none border-0 bg-transparent p-0" };
47
98
  />
48
99
  </div>
49
100
  <div v-if="selected" v-bind="contentProps">
50
- <pre v-bind="preProps">{{ selected.code }}</pre>
101
+ <pre v-bind="preProps"><code v-if="highlightedCode" class="selaras-code-tree-code" :data-selaras-code-mode="codeMode" v-html="highlightedCode" /><code v-else>{{ selected.code }}</code></pre>
51
102
  </div>
52
103
  <div v-else v-bind="emptyProps">
53
104
  {{ messages.selectFile }}
@@ -4,6 +4,8 @@ import type { FileTreeNode } from './FileTree.vue.js';
4
4
  export interface CodeTreeFile extends FileTreeNode {
5
5
  /** The file's content - read for a leaf node once selected, ignored for a directory. */
6
6
  code?: string;
7
+ /** Overrides the language inferred from the filename. */
8
+ language?: string;
7
9
  children?: CodeTreeFile[];
8
10
  }
9
11
  export interface CodeTreeProps {