@getcortexlabs/mavis-core 0.1.0-alpha.1 → 0.1.0-alpha.2

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/src/index.ts CHANGED
@@ -16,4 +16,7 @@ export { sliderScale } from './slider.js'
16
16
  export { resolveDimension, resolveRadius, nonNegative } from './layout.js'
17
17
  export type { Dimension } from './layout.js'
18
18
  export { validateInput } from './input.js'
19
+ export { icons, iconFontVersion } from './icons.js'
20
+ export { iconGlyph, iconFontFamily, iconSizes, resolveIconSize, checkIconOnly } from './icon.js'
21
+ export type { IconName, IconSize } from './icon.js'
19
22
  export type { InputHandle } from './input.js'
@@ -22,6 +22,7 @@ declare module '@vue/runtime-core' {
22
22
  MButton: DefineComponent<ComponentProps['MButton'] & Action>
23
23
  MSlider: DefineComponent<ComponentProps['MSlider'] & Model>
24
24
  MInput: DefineComponent<ComponentProps['MInput'] & InputEvents>
25
+ MIcon: DefineComponent<ComponentProps['MIcon']>
25
26
  MReveal: DefineComponent<ComponentProps['MReveal']>
26
27
  }
27
28
  }
package/src/vue/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  export { createThemeContext, themeKey, useMavisTheme } from './theme.js'
2
- export type { MavisThemeContext } from './theme.js'
2
+ export type {
3
+ MavisThemeContext,
4
+ Appearance,
5
+ AppearanceMode,
6
+ AppearanceSource,
7
+ ThemeContextOptions,
8
+ } from './theme.js'
3
9
  export { layoutDirection, useDefaultShrink } from './layout-context.js'
4
10
  export { textContent } from './text-content.js'
5
11
  export type {} from './global-components.js'
@@ -14,6 +20,7 @@ export {
14
20
  sliderProps,
15
21
  inputProps,
16
22
  inputEmits,
23
+ iconProps,
17
24
  screenProps,
18
25
  revealProps,
19
26
  componentProps,
package/src/vue/props.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ExtractPropTypes, ExtractPublicPropTypes, PropType } from '@vue/runtime-core'
2
2
  import type { Palette, Spacing, Radius } from '../theme/types.js'
3
3
  import type { Dimension } from '../layout.js'
4
+ import { iconSizes, type IconName, type IconSize } from '../icon.js'
4
5
 
5
6
  function choice<T extends string>(values: readonly T[], defaultValue: T) {
6
7
  return {
@@ -77,32 +78,32 @@ export const buttonProps = {
77
78
  disabled: Boolean,
78
79
  loading: Boolean,
79
80
  loadingText: { type: String, default: 'Loading…' },
81
+ icon: { type: String as PropType<IconName>, default: undefined },
82
+ trailingIcon: { type: String as PropType<IconName>, default: undefined },
80
83
  }
84
+ const tones: readonly (keyof Palette)[] = [
85
+ 'background',
86
+ 'surface',
87
+ 'elevated',
88
+ 'text',
89
+ 'muted',
90
+ 'primary',
91
+ 'onPrimary',
92
+ 'primarySoft',
93
+ 'accent',
94
+ 'onAccent',
95
+ 'accentSoft',
96
+ 'border',
97
+ 'success',
98
+ 'danger',
99
+ ]
81
100
  export const textProps = {
82
101
  ...viewProps,
83
102
  ...flexProps,
84
103
  text: { type: String, default: undefined },
85
104
  as: choice(['p', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const, 'p'),
86
105
  variant: choice(['display', 'title', 'heading', 'body', 'caption', 'overline'] as const, 'body'),
87
- tone: choice<keyof Palette>(
88
- [
89
- 'background',
90
- 'surface',
91
- 'elevated',
92
- 'text',
93
- 'muted',
94
- 'primary',
95
- 'onPrimary',
96
- 'primarySoft',
97
- 'accent',
98
- 'onAccent',
99
- 'accentSoft',
100
- 'border',
101
- 'success',
102
- 'danger',
103
- ],
104
- 'text',
105
- ),
106
+ tone: choice<keyof Palette>(tones, 'text'),
106
107
  align: choice(['left', 'center', 'right'] as const, 'left'),
107
108
  wrap: { type: Boolean, default: true },
108
109
  }
@@ -139,6 +140,8 @@ export const inputProps = {
139
140
  },
140
141
  autocorrect: { type: Boolean, default: undefined },
141
142
  returnKey: choice(['done', 'next', 'go', 'search', 'send'] as const, 'done'),
143
+ icon: { type: String as PropType<IconName>, default: undefined },
144
+ trailingIcon: { type: String as PropType<IconName>, default: undefined },
142
145
  }
143
146
  export const inputEmits = {
144
147
  'update:modelValue': (value: string) => typeof value === 'string',
@@ -146,6 +149,18 @@ export const inputEmits = {
146
149
  blur: () => true,
147
150
  submit: (value: string) => typeof value === 'string',
148
151
  }
152
+ export const iconProps = {
153
+ id: viewProps.id,
154
+ name: { type: String as PropType<IconName>, required: true as const },
155
+ size: {
156
+ type: [String, Number] as PropType<IconSize>,
157
+ default: 'md',
158
+ validator: (value: IconSize) => typeof value === 'number' || value in iconSizes,
159
+ },
160
+ tone: choice<keyof Palette>(tones, 'text'),
161
+ /** Accessible name. Without it the icon is decorative and hidden from assistive technology. */
162
+ label: { type: String, default: undefined },
163
+ }
149
164
  export const screenProps = { ...viewProps, scroll: Boolean }
150
165
  export const revealProps = { ...columnProps, show: Boolean }
151
166
  export const actionEmits = { click: () => true }
@@ -161,6 +176,7 @@ export const componentProps = {
161
176
  MButton: buttonProps,
162
177
  MSlider: sliderProps,
163
178
  MInput: inputProps,
179
+ MIcon: iconProps,
164
180
  MReveal: revealProps,
165
181
  }
166
182
  export type BoxProps = ExtractPropTypes<typeof boxProps>
package/src/vue/theme.ts CHANGED
@@ -2,17 +2,68 @@ import { inject, readonly, shallowRef, type InjectionKey } from '@vue/runtime-co
2
2
  import { mavis } from '../theme/themes.js'
3
3
  import type { MavisTheme } from '../theme/types.js'
4
4
 
5
- export function createThemeContext(initial: MavisTheme) {
5
+ export type Appearance = MavisTheme['appearance']
6
+ export type AppearanceMode = Appearance | 'system'
7
+
8
+ /** Platform access to the device appearance; `current` is undefined while it is unknown, such as during SSR. */
9
+ export interface AppearanceSource {
10
+ current(): Appearance | undefined
11
+ observe(listener: (appearance: Appearance) => void): () => void
12
+ }
13
+
14
+ export interface ThemeContextOptions {
15
+ theme?: MavisTheme
16
+ darkTheme?: MavisTheme
17
+ appearance?: AppearanceMode
18
+ source?: AppearanceSource
19
+ }
20
+
21
+ const modes: readonly AppearanceMode[] = ['system', 'light', 'dark']
22
+
23
+ function checkMode(mode: AppearanceMode) {
24
+ if (!modes.includes(mode)) throw new RangeError(`Unknown appearance mode: ${String(mode)}`)
25
+ return mode
26
+ }
27
+
28
+ export function createThemeContext(options: MavisTheme | ThemeContextOptions = {}) {
29
+ const config: ThemeContextOptions = 'colors' in options ? { theme: options } : options
30
+ const initial = config.theme ?? mavis
31
+ const themes: Partial<Record<Appearance, MavisTheme>> = { [initial.appearance]: initial }
32
+ if (config.darkTheme) themes.dark = config.darkTheme
33
+ const appearance = shallowRef(checkMode(config.appearance ?? 'system'))
6
34
  const theme = shallowRef(initial)
35
+ const resolve = () => {
36
+ const wanted =
37
+ appearance.value === 'system'
38
+ ? (config.source?.current() ?? initial.appearance)
39
+ : appearance.value
40
+ theme.value = themes[wanted] ?? themes[wanted === 'dark' ? 'light' : 'dark'] ?? initial
41
+ }
42
+ resolve()
43
+ const stop = config.source?.observe(() => {
44
+ if (appearance.value === 'system') resolve()
45
+ })
7
46
  return {
8
47
  theme: readonly(theme),
48
+ /** The selection: `system` follows the device; `light` and `dark` are explicit overrides. */
49
+ appearance: readonly(appearance),
50
+ setAppearance(mode: AppearanceMode) {
51
+ appearance.value = checkMode(mode)
52
+ resolve()
53
+ },
54
+ /** Registers the theme for its own appearance and pins that appearance. */
9
55
  setTheme(next: MavisTheme) {
10
- theme.value = next
56
+ themes[next.appearance] = next
57
+ appearance.value = next.appearance
58
+ resolve()
59
+ },
60
+ dispose() {
61
+ stop?.()
11
62
  },
12
63
  }
13
64
  }
14
-
15
65
  export type MavisThemeContext = ReturnType<typeof createThemeContext>
66
+
16
67
  export const themeKey: InjectionKey<MavisThemeContext> = Symbol('mavis.theme')
17
68
 
18
69
  /** Without the plugin, individually imported components use the default theme. */