@ui0/theme 0.0.0-stage → 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/README.md +113 -2
- package/dist/backdrop.module.css +1 -0
- package/dist/backdrop.scss +7 -0
- package/dist/color.inject.module.css +7 -0
- package/dist/color.module.css +3 -0
- package/dist/color.scss +84 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +53 -0
- package/dist/components/style.css +1 -0
- package/dist/components/theme-provider.keys.d.ts +4 -0
- package/dist/components/theme-provider.keys.d.ts.map +1 -0
- package/dist/components/theme-provider.module.css +1 -0
- package/dist/components/theme-provider.types.d.ts +18 -0
- package/dist/components/theme-provider.types.d.ts.map +1 -0
- package/dist/components/theme-provider.vue.d.ts +19 -0
- package/dist/config.default.d.mts +4 -0
- package/dist/config.default.d.mts.map +1 -0
- package/dist/config.default.mjs +73 -0
- package/dist/config.default.mjs.map +1 -0
- package/dist/disabled.inject.module.css +5 -0
- package/dist/disabled.module.css +1 -0
- package/dist/disabled.scss +3 -0
- package/dist/display.module.css +2 -0
- package/dist/display.scss +63 -0
- package/dist/duration.scss +17 -0
- package/dist/elevation.module.css +33 -0
- package/dist/elevation.scss +46 -0
- package/dist/generate-BPYE5ALG.mjs +1037 -0
- package/dist/generate-BPYE5ALG.mjs.map +1 -0
- package/dist/generator/cli.d.mts +1 -0
- package/dist/generator/cli.mjs +103 -0
- package/dist/generator/cli.mjs.map +1 -0
- package/dist/generator/vite.d.mts +18 -0
- package/dist/generator/vite.d.mts.map +1 -0
- package/dist/generator/vite.mjs +117 -0
- package/dist/generator/vite.mjs.map +1 -0
- package/dist/index.d.mts +339 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +909 -0
- package/dist/index.mjs.map +1 -0
- package/dist/is.scss +185 -0
- package/dist/load-config-64q6nzFy.mjs +48 -0
- package/dist/load-config-64q6nzFy.mjs.map +1 -0
- package/dist/ripple.scss +57 -0
- package/dist/round.inject.module.css +92 -0
- package/dist/round.module.css +14 -0
- package/dist/round.scss +90 -0
- package/dist/size.inject.module.css +50 -0
- package/dist/size.module.css +13 -0
- package/dist/size.scss +28 -0
- package/dist/theme.scss +16 -0
- package/dist/typography.inject.module.css +81 -0
- package/dist/typography.module.css +32 -0
- package/dist/typography.scss +21 -0
- package/dist/util.scss +78 -0
- package/dist/vars.scss +0 -0
- package/package.json +96 -4
- package/src/contracts/backdrop/backdrop.contract.ts +44 -0
- package/src/contracts/backdrop/backdrop.generator.ts +82 -0
- package/src/contracts/backdrop/backdrop.keys.ts +7 -0
- package/src/contracts/backdrop/backdrop.module.css +3 -0
- package/src/contracts/backdrop/backdrop.scss +7 -0
- package/src/contracts/backdrop/backdrop.types.ts +45 -0
- package/src/contracts/backdrop/index.ts +21 -0
- package/src/contracts/color/color.contract.spec.ts +75 -0
- package/src/contracts/color/color.contract.ts +97 -0
- package/src/contracts/color/color.generator.spec.ts +73 -0
- package/src/contracts/color/color.generator.ts +122 -0
- package/src/contracts/color/color.gradient.spec.ts +32 -0
- package/src/contracts/color/color.gradient.ts +52 -0
- package/src/contracts/color/color.inject.module.css +5 -0
- package/src/contracts/color/color.keys.ts +7 -0
- package/src/contracts/color/color.module.css +5 -0
- package/src/contracts/color/color.scss +84 -0
- package/src/contracts/color/color.serialize.spec.ts +57 -0
- package/src/contracts/color/color.serialize.ts +35 -0
- package/src/contracts/color/color.token.spec.ts +57 -0
- package/src/contracts/color/color.token.ts +89 -0
- package/src/contracts/color/color.types.ts +82 -0
- package/src/contracts/color/index.md +87 -0
- package/src/contracts/color/index.ts +25 -0
- package/src/contracts/contract.ts +115 -0
- package/src/contracts/disabled/disabled.contract.ts +32 -0
- package/src/contracts/disabled/disabled.generator.ts +32 -0
- package/src/contracts/disabled/disabled.inject.module.css +5 -0
- package/src/contracts/disabled/disabled.keys.ts +6 -0
- package/src/contracts/disabled/disabled.module.css +3 -0
- package/src/contracts/disabled/disabled.scss +3 -0
- package/src/contracts/disabled/disabled.types.ts +19 -0
- package/src/contracts/disabled/index.ts +21 -0
- package/src/contracts/display/display.contract.ts +41 -0
- package/src/contracts/display/display.generator.ts +57 -0
- package/src/contracts/display/display.instance.ts +173 -0
- package/src/contracts/display/display.keys.ts +9 -0
- package/src/contracts/display/display.module.css +4 -0
- package/src/contracts/display/display.scss +63 -0
- package/src/contracts/display/display.types.ts +75 -0
- package/src/contracts/display/index.ts +27 -0
- package/src/contracts/elevation/elevation.contract.ts +37 -0
- package/src/contracts/elevation/elevation.generator.ts +71 -0
- package/src/contracts/elevation/elevation.keys.ts +6 -0
- package/src/contracts/elevation/elevation.module.css +35 -0
- package/src/contracts/elevation/elevation.scss +46 -0
- package/src/contracts/elevation/elevation.types.ts +21 -0
- package/src/contracts/elevation/index.ts +20 -0
- package/src/contracts/generator.ts +38 -0
- package/src/contracts/index.md +48 -0
- package/src/contracts/index.ts +37 -0
- package/src/contracts/round/index.ts +21 -0
- package/src/contracts/round/round.contract.ts +75 -0
- package/src/contracts/round/round.generator.ts +33 -0
- package/src/contracts/round/round.inject.module.scss +36 -0
- package/src/contracts/round/round.keys.ts +7 -0
- package/src/contracts/round/round.module.css +16 -0
- package/src/contracts/round/round.scss +90 -0
- package/src/contracts/round/round.types.ts +44 -0
- package/src/contracts/size/index.ts +22 -0
- package/src/contracts/size/size.contract.ts +98 -0
- package/src/contracts/size/size.generator.spec.ts +101 -0
- package/src/contracts/size/size.generator.ts +51 -0
- package/src/contracts/size/size.inject.module.scss +12 -0
- package/src/contracts/size/size.keys.ts +8 -0
- package/src/contracts/size/size.module.css +15 -0
- package/src/contracts/size/size.scss +28 -0
- package/src/contracts/size/size.types.ts +63 -0
- package/src/contracts/size/units.scss +17 -0
- package/src/contracts/style/index.ts +11 -0
- package/src/contracts/style/style.generator.ts +36 -0
- package/src/contracts/style/style.key.ts +4 -0
- package/src/contracts/style/style.types.ts +16 -0
- package/src/contracts/typography/index.ts +26 -0
- package/src/contracts/typography/typography.contract.ts +52 -0
- package/src/contracts/typography/typography.generator.spec.ts +48 -0
- package/src/contracts/typography/typography.generator.ts +44 -0
- package/src/contracts/typography/typography.inject.module.css +54 -0
- package/src/contracts/typography/typography.keys.ts +12 -0
- package/src/contracts/typography/typography.module.css +36 -0
- package/src/contracts/typography/typography.scss +21 -0
- package/src/contracts/typography/typography.types.ts +90 -0
- package/src/generator/theme.default.css +657 -0
- package/src/styles/duration.scss +17 -0
- package/src/styles/index.md +18 -0
- package/src/styles/is.scss +185 -0
- package/src/styles/ripple.scss +57 -0
- package/src/styles/theme.scss +16 -0
- package/src/styles/util.scss +78 -0
- package/src/styles/vars.scss +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ThemeContract } from '@ui0/types'
|
|
2
|
+
|
|
3
|
+
import { ThemeContractFlags } from '../../constants'
|
|
4
|
+
import { disabledDeal } from './disabled.contract'
|
|
5
|
+
import { DisabledContractKey, DisabledRefKey } from './disabled.keys'
|
|
6
|
+
import type { DisabledProps } from './disabled.types'
|
|
7
|
+
|
|
8
|
+
export { default as $style } from './disabled.module.css'
|
|
9
|
+
export type * from './disabled.types'
|
|
10
|
+
export * from './disabled.keys'
|
|
11
|
+
|
|
12
|
+
export const DisabledPropsKeys: ReadonlyArray<keyof DisabledProps> = ['disabled']
|
|
13
|
+
|
|
14
|
+
export const disabled: ThemeContract = {
|
|
15
|
+
key: DisabledContractKey,
|
|
16
|
+
name: 'disabled',
|
|
17
|
+
ref: DisabledRefKey,
|
|
18
|
+
flag: ThemeContractFlags.dealer + ThemeContractFlags.generator,
|
|
19
|
+
deal: disabledDeal,
|
|
20
|
+
props: DisabledPropsKeys
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// The display contract's processing endpoint: closes over the provider and
|
|
2
|
+
// returns the deal that maps the `display` prop onto classes/styles at
|
|
3
|
+
// runtime, resolving `inherit` against the provided context ref.
|
|
4
|
+
import type { ClassList, CSSModuleClassList } from '@ui0/types'
|
|
5
|
+
import type { ContractProvider, ThemeContractDeal } from '@ui0/types'
|
|
6
|
+
import { mergeCssModulesStyles } from '@ui0/util'
|
|
7
|
+
import type { Ref } from 'vue'
|
|
8
|
+
|
|
9
|
+
import { EMPTY_OBJ } from '../../constants'
|
|
10
|
+
import type { DisplayProps } from './display.types'
|
|
11
|
+
|
|
12
|
+
import $display from './display.module.css'
|
|
13
|
+
|
|
14
|
+
// the prop -> class wiring: resolves the prop value to the hashed class from
|
|
15
|
+
// the module map (merged with the component's own $style)
|
|
16
|
+
function displayClasses(props: DisplayProps, styles?: CSSModuleClassList): ClassList {
|
|
17
|
+
const $ = mergeCssModulesStyles($display, styles)
|
|
18
|
+
const out: ClassList = []
|
|
19
|
+
if (props.display && $.displayed) out.push($.displayed)
|
|
20
|
+
return out
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function displayDeal(instance: ContractProvider): ThemeContractDeal {
|
|
24
|
+
const props = instance.props
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
classes: () => {
|
|
28
|
+
if (!props.display) return instance.inject([])
|
|
29
|
+
|
|
30
|
+
if (props.display === 'inherit') {
|
|
31
|
+
const ctx = instance.context as Record<string, Ref<any> | undefined> | null
|
|
32
|
+
if (ctx?.display) {
|
|
33
|
+
return instance.inject(displayClasses({ display: ctx.display.value }, instance.$styles))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return instance.inject(displayClasses(props as DisplayProps, instance.$styles))
|
|
38
|
+
},
|
|
39
|
+
styles: () => instance.inject(EMPTY_OBJ)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// The display contract's generator endpoint: turns the contract's slice of
|
|
2
|
+
// the appearance config (DisplayThemeAppearanceConfig) into css vars and
|
|
3
|
+
// setter classes. Values are authored in the theme config; this generator
|
|
4
|
+
// only knows the wiring.
|
|
5
|
+
import type { CSSModuleClassList, StyleList, VarList } from '@ui0/types'
|
|
6
|
+
import type { ThemeContractGenerator } from '@ui0/types'
|
|
7
|
+
import { toUnits } from '@ui0/util'
|
|
8
|
+
|
|
9
|
+
import type { ContractContext, ContractFactory } from '../generator'
|
|
10
|
+
import type { DisplayThemeAppearanceConfig } from './display.types'
|
|
11
|
+
|
|
12
|
+
// the logical class names shipped in display.module.css - class() re-exports
|
|
13
|
+
// the registry map, so the logical names resolve to the distiller hashes.
|
|
14
|
+
import $display from './display.module.css'
|
|
15
|
+
|
|
16
|
+
const displayGenerator: ContractFactory = (ctx: ContractContext): ThemeContractGenerator => {
|
|
17
|
+
const { config } = ctx
|
|
18
|
+
|
|
19
|
+
function vars(): Record<string, VarList> {
|
|
20
|
+
// after registration (see display.types.ts) this reads
|
|
21
|
+
// `config.display`; typed loosely until the contract is named.
|
|
22
|
+
const display = (config as { display?: DisplayThemeAppearanceConfig }).display
|
|
23
|
+
if (display?.base === undefined) return {}
|
|
24
|
+
return { display: { base: display.base } }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
namespace: 'd',
|
|
29
|
+
vars: (scheme) => (scheme ? {} : vars()),
|
|
30
|
+
css: () => `.${$display.displayed} { /* point the component slot at the theme var */ }`,
|
|
31
|
+
localScss: () => {
|
|
32
|
+
const brp = ctx.settings.display?.breakpoints
|
|
33
|
+
const out: StyleList = {}
|
|
34
|
+
|
|
35
|
+
if (brp) {
|
|
36
|
+
Object.keys(brp).forEach((k) => {
|
|
37
|
+
out[`$${k}`] = toUnits(brp[k])
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
return out
|
|
41
|
+
},
|
|
42
|
+
class: (): CSSModuleClassList => ({ ...$display }),
|
|
43
|
+
styles: () => {
|
|
44
|
+
const brp = ctx.settings.display?.breakpoints
|
|
45
|
+
const out: StyleList = {}
|
|
46
|
+
|
|
47
|
+
if (brp) {
|
|
48
|
+
Object.keys(brp).forEach((k) => {
|
|
49
|
+
out[`--0-display-${k}`] = toUnits(brp[k])
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
return out
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default displayGenerator
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { Ui0ThemeContractSettings, EventHandler } from '@ui0/types'
|
|
2
|
+
import {
|
|
3
|
+
getClientHeight,
|
|
4
|
+
getClientWidth,
|
|
5
|
+
isBrowser,
|
|
6
|
+
objectMerge,
|
|
7
|
+
parseUnits,
|
|
8
|
+
RecordContainer,
|
|
9
|
+
type RecordContainerImpl
|
|
10
|
+
} from '@ui0/util'
|
|
11
|
+
import { ref, type Ref, type WatchHandle } from 'vue'
|
|
12
|
+
|
|
13
|
+
import cfg from '../../config.default'
|
|
14
|
+
import type {
|
|
15
|
+
DisplayBreakpoint,
|
|
16
|
+
DisplayBreakpoints,
|
|
17
|
+
DisplayImpl,
|
|
18
|
+
DisplayMeasure,
|
|
19
|
+
DisplayOperation,
|
|
20
|
+
DisplayWatcher
|
|
21
|
+
} from './display.types'
|
|
22
|
+
|
|
23
|
+
export class Display implements DisplayImpl {
|
|
24
|
+
breakpoints: DisplayBreakpoints
|
|
25
|
+
_watchers: DisplayWatcher[]
|
|
26
|
+
_watcher: WatchHandle | null
|
|
27
|
+
_activated: boolean
|
|
28
|
+
_listeners: RecordContainerImpl<any>
|
|
29
|
+
_width: Ref<number> | null
|
|
30
|
+
_height: Ref<number> | null
|
|
31
|
+
_resizeHandler: EventHandler
|
|
32
|
+
|
|
33
|
+
constructor(config: Ui0ThemeContractSettings['display']) {
|
|
34
|
+
this.breakpoints = objectMerge(cfg.contracts.display?.breakpoints, config?.breakpoints ?? {})
|
|
35
|
+
|
|
36
|
+
this._watchers = []
|
|
37
|
+
this._watcher = null
|
|
38
|
+
this._width = null
|
|
39
|
+
this._height = null
|
|
40
|
+
this._activated = false
|
|
41
|
+
this._resizeHandler = () => this.update()
|
|
42
|
+
this._listeners = new RecordContainer()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public update() {
|
|
46
|
+
if (this._width && this._height) {
|
|
47
|
+
this._width.value = getClientWidth()
|
|
48
|
+
this._height.value = getClientHeight()
|
|
49
|
+
}
|
|
50
|
+
let wasChanged = false
|
|
51
|
+
|
|
52
|
+
this._watchers.forEach((watcher) => {
|
|
53
|
+
const measure = watcher.measure === 'width' ? this._width : this._height
|
|
54
|
+
const oldValue = watcher.value.value
|
|
55
|
+
|
|
56
|
+
if (watcher.operation === 'between') {
|
|
57
|
+
const breakpoint1 = this.getBreakpointValue(String(watcher.points[0]))
|
|
58
|
+
const breakpoint2 = this.getBreakpointValue(String(watcher.points[1]))
|
|
59
|
+
|
|
60
|
+
if (measure && breakpoint1 && breakpoint2) {
|
|
61
|
+
const b1 = parseUnits(String(breakpoint1)).value
|
|
62
|
+
const b2 = parseUnits(String(breakpoint2)).value
|
|
63
|
+
// console.log('we', b1, b2)
|
|
64
|
+
if (b1 > b2) watcher.value.value = measure.value > b2 && measure.value < b1
|
|
65
|
+
if (b1 < b2) watcher.value.value = measure.value > b1 && measure.value < b2
|
|
66
|
+
|
|
67
|
+
if (watcher.value.value !== oldValue) {
|
|
68
|
+
wasChanged = true
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (watcher.operation === 'less' || watcher.operation === 'greater') {
|
|
73
|
+
const breakpoint1 = this.getBreakpointValue(String(watcher.points[0]))
|
|
74
|
+
if (breakpoint1 && measure) {
|
|
75
|
+
const b1 = parseUnits(String(breakpoint1)).value
|
|
76
|
+
watcher.value.value = watcher.operation === 'less' ? measure.value < b1 : measure.value > b1
|
|
77
|
+
|
|
78
|
+
if (watcher.value.value !== oldValue) {
|
|
79
|
+
wasChanged = true
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
if (wasChanged) {
|
|
86
|
+
this._listeners.items.forEach((item) => item())
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public addUpdateListener(fn: AnyFn): number {
|
|
91
|
+
return this._listeners.add(fn)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public removeUpdateListener(index: number) {
|
|
95
|
+
this._listeners.remove(index)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public less(point: DisplayBreakpoint, measure: DisplayMeasure = 'width') {
|
|
99
|
+
const out = this.addWatcher('less', [point], measure)
|
|
100
|
+
this.activate()
|
|
101
|
+
return out
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public between(point1: DisplayBreakpoint, point2: DisplayBreakpoint, measure: DisplayMeasure = 'width') {
|
|
105
|
+
const out = this.addWatcher('between', [point1, point2], measure)
|
|
106
|
+
this.activate()
|
|
107
|
+
this.update()
|
|
108
|
+
return out
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public greater(point: DisplayBreakpoint, measure: DisplayMeasure = 'width') {
|
|
112
|
+
const out = this.addWatcher('greater', [point], measure)
|
|
113
|
+
this.activate()
|
|
114
|
+
return out
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public activate() {
|
|
118
|
+
// if (this._activated) return
|
|
119
|
+
|
|
120
|
+
if (!this._width) this._width = ref(0)
|
|
121
|
+
if (!this._height) this._height = ref(0)
|
|
122
|
+
|
|
123
|
+
if (isBrowser && !this._activated) window.addEventListener('resize', this._resizeHandler, { passive: true })
|
|
124
|
+
|
|
125
|
+
this.update()
|
|
126
|
+
this._activated = true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public deactivate() {
|
|
130
|
+
this._activated = false
|
|
131
|
+
if (isBrowser) window.removeEventListener('resize', this._resizeHandler)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private getBreakpointValue(name: string): number {
|
|
135
|
+
// console.log('breaks', Object.keys(_breakpoints).length)
|
|
136
|
+
return +this.breakpoints[name]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private addWatcher(
|
|
140
|
+
operation: DisplayOperation,
|
|
141
|
+
points: DisplayBreakpoint[],
|
|
142
|
+
measure: DisplayMeasure = 'width'
|
|
143
|
+
): Ref<boolean> {
|
|
144
|
+
let outRef: Ref<boolean> | undefined = undefined
|
|
145
|
+
|
|
146
|
+
this._watchers.forEach((watcher) => {
|
|
147
|
+
const isMultiValued = watcher.points.length > 1
|
|
148
|
+
if (watcher.operation === operation) {
|
|
149
|
+
if (isMultiValued) {
|
|
150
|
+
if (watcher.points[0] === points[0] && watcher.points[1] === points[1]) {
|
|
151
|
+
console.log('setting existing ', watcher.value)
|
|
152
|
+
outRef = watcher.value
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
if (watcher.points[0] === points[0]) {
|
|
156
|
+
outRef = watcher.value
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
if (!outRef) {
|
|
162
|
+
outRef = ref(false)
|
|
163
|
+
this._watchers.push({
|
|
164
|
+
measure,
|
|
165
|
+
points,
|
|
166
|
+
operation,
|
|
167
|
+
value: outRef
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return outRef
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ThemeContract } from '@ui0/types'
|
|
2
|
+
import type { InjectionKey, Ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { DisplayImpl, DisplayProp, DisplayReturn } from './display.types'
|
|
5
|
+
|
|
6
|
+
/** context key the deal resolves `inherit` against (see useThemeContract) */
|
|
7
|
+
export const DisplayRefKey = Symbol('DisplayRefKey') as InjectionKey<Ref<DisplayProp | undefined>>
|
|
8
|
+
export const DisplayContractKey = Symbol('DisplayContractKey') as InjectionKey<ThemeContract>
|
|
9
|
+
export const DisplayKey = Symbol('DisplayContractKey') as InjectionKey<DisplayImpl>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
|
|
2
|
+
// Component-namespace wiring for the display contract: the generator emits
|
|
3
|
+
// `--0-display-base` on the theme blocks; this mixin mirrors it into the
|
|
4
|
+
// component namespace so a component can consume (and locally override) it.
|
|
5
|
+
@mixin use() {
|
|
6
|
+
--0c-display: var(--0-display-base, initial);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@mixin wh($width, $height) {
|
|
10
|
+
$max-w: ' and (max-width: #{$width})';
|
|
11
|
+
$max-h: ' and (max-height: #{$height})';
|
|
12
|
+
@media only screen#{$max-h}#{$max-w} {
|
|
13
|
+
@content;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
@mixin w-between($max, $min) {
|
|
17
|
+
$min-w: ' and (min-width: #{$min})';
|
|
18
|
+
$max-w: ' and (max-width: #{$max})';
|
|
19
|
+
@if $max == 0 and $min == 0 {
|
|
20
|
+
} @else {
|
|
21
|
+
@if $max == 0 {
|
|
22
|
+
$max-w: ' ';
|
|
23
|
+
}
|
|
24
|
+
@media only screen#{$min-w}#{$max-w} {
|
|
25
|
+
@content;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@mixin w-greater($val) {
|
|
31
|
+
@include w-between(0, $val) {
|
|
32
|
+
@content;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@mixin w-less($val) {
|
|
37
|
+
@include w-between($val, 0) {
|
|
38
|
+
@content;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
@mixin h-between($max, $min) {
|
|
42
|
+
$minh: ' and (min-height: #{$min})';
|
|
43
|
+
$maxh: ' and (max-height: #{$max})';
|
|
44
|
+
@if $max == 0 and $min == 0 {
|
|
45
|
+
} @else {
|
|
46
|
+
@if $max == 0 {
|
|
47
|
+
$maxh: ' ';
|
|
48
|
+
}
|
|
49
|
+
@media only screen#{$minh}#{$maxh} {
|
|
50
|
+
@content;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
@mixin h-greater($val) {
|
|
55
|
+
@include h-between(0, $val) {
|
|
56
|
+
@content;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
@mixin h-less($val) {
|
|
60
|
+
@include h-between($val, 0) {
|
|
61
|
+
@content;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// CONTRACT TEMPLATE - a skeleton for new theme contracts, consumed by
|
|
3
|
+
// scripts/gen-contract.ts in the templates package:
|
|
4
|
+
//
|
|
5
|
+
// pnpm contract:gen
|
|
6
|
+
//
|
|
7
|
+
// The generator copies this folder into <package>/src/contracts/<name>/
|
|
8
|
+
// (the theme package ships the contracts folder), substituting display /
|
|
9
|
+
// Display / display / d in file bodies and names. After generation:
|
|
10
|
+
//
|
|
11
|
+
// 1. add the contract to src/contracts/index.ts (registry)
|
|
12
|
+
// 2. add <kebab>Generator to src/generator.ts (contractGenerators)
|
|
13
|
+
// 3. fill in the prop values / appearance config below
|
|
14
|
+
//
|
|
15
|
+
// The package itself stays unaware of the contract - types land in the
|
|
16
|
+
// registries via interface merging only.
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
import type { LiteralUnion, MaybeUnits, Numberish, UpdateFn } from '@ui0/types'
|
|
19
|
+
import type { Ref } from 'vue'
|
|
20
|
+
|
|
21
|
+
// 1. the values the prop accepts
|
|
22
|
+
export type DisplayProp = LiteralUnion<'some' | 'display' | 'values'>
|
|
23
|
+
export type DisplayOperation = 'greater' | 'less' | 'equal' | 'between'
|
|
24
|
+
|
|
25
|
+
export type DisplayMeasure = 'width' | 'height'
|
|
26
|
+
|
|
27
|
+
export type DisplayBreakpoint = string | 'xxs' | 's' | 'xs' | 'xxl' | 'lg' | 'xl' | 'hd' | 'fhd' | 'qhd' | 'uhd'
|
|
28
|
+
export type DisplayBreakpoints = Record<DisplayBreakpoint, Numberish>
|
|
29
|
+
|
|
30
|
+
// 2. the props this contract supplies to components
|
|
31
|
+
export interface DisplayProps {
|
|
32
|
+
// display?: DisplayProp
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 3. the contract's settings inside a theme preset (appearance config) -
|
|
36
|
+
// what the generator turns into css vars
|
|
37
|
+
export interface DisplayThemeAppearanceConfig {
|
|
38
|
+
/** e.g. a base value the generator emits as a css var */
|
|
39
|
+
base?: MaybeUnits
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 4. registration: announces this contract to the package registries.
|
|
43
|
+
declare module '@ui0/types/theme' {
|
|
44
|
+
interface Ui0ThemePresetAppearanceConfig {
|
|
45
|
+
display?: DisplayThemeAppearanceConfig
|
|
46
|
+
}
|
|
47
|
+
interface Ui0ThemeContractSettings {
|
|
48
|
+
display?: {
|
|
49
|
+
breakpoints: DisplayBreakpoints
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
interface Ui0ThemeContracts extends DisplayProps {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface DisplayWatcher {
|
|
56
|
+
points: Array<any>
|
|
57
|
+
measure: DisplayMeasure
|
|
58
|
+
operation: DisplayOperation
|
|
59
|
+
value: Ref<boolean>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface DisplayReturn {
|
|
63
|
+
breakpoints: DisplayBreakpoints
|
|
64
|
+
between: (point: DisplayBreakpoint, b: DisplayBreakpoint, measure?: DisplayMeasure) => Ref<boolean>
|
|
65
|
+
greater: (point: DisplayBreakpoint, measure?: DisplayMeasure) => Ref<boolean>
|
|
66
|
+
less: (point: DisplayBreakpoint, measure?: DisplayMeasure) => Ref<boolean>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DisplayImpl extends DisplayReturn {
|
|
70
|
+
activate: () => void
|
|
71
|
+
deactivate: () => void
|
|
72
|
+
update: () => void
|
|
73
|
+
addUpdateListener: (fn: UpdateFn) => number
|
|
74
|
+
removeUpdateListener: (id: number) => void
|
|
75
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ThemeContract } from '@ui0/types'
|
|
2
|
+
|
|
3
|
+
// The display contract's registry entry, generated from
|
|
4
|
+
// packages/templates/contract. Wiring left (see the generator output):
|
|
5
|
+
// 1. add `display` to src/contracts/index.ts (next to round/size/...)
|
|
6
|
+
// 2. add displayGenerator to src/generator.ts (contractGenerators)
|
|
7
|
+
import { ThemeContractFlags } from '../../constants'
|
|
8
|
+
import { displayDeal } from './display.contract'
|
|
9
|
+
import { DisplayContractKey, DisplayRefKey } from './display.keys'
|
|
10
|
+
import type { DisplayProps } from './display.types'
|
|
11
|
+
|
|
12
|
+
export { default as $style } from './display.module.css'
|
|
13
|
+
export type * from './display.types'
|
|
14
|
+
export * from './display.keys'
|
|
15
|
+
|
|
16
|
+
export const DisplayPropsKeys: ReadonlyArray<keyof DisplayProps> = [
|
|
17
|
+
// 'display'
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
export const display: ThemeContract = {
|
|
21
|
+
key: DisplayContractKey,
|
|
22
|
+
name: 'display',
|
|
23
|
+
ref: DisplayRefKey,
|
|
24
|
+
deal: displayDeal,
|
|
25
|
+
flag: ThemeContractFlags.generator,
|
|
26
|
+
props: DisplayPropsKeys
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The elevation contract's processing endpoint: the `elevation` prop picks a
|
|
2
|
+
// step of the shadow scale; the deal resolves it to the hashed step class
|
|
3
|
+
// (`elv1..steps`, `n` suffix for inset), `inherit` reading the provided ref.
|
|
4
|
+
import type { ClassList, CSSModuleClassList } from '@ui0/types'
|
|
5
|
+
import type { ContractProvider, ThemeContractDeal } from '@ui0/types'
|
|
6
|
+
import { mergeCssModulesStyles } from '@ui0/util'
|
|
7
|
+
|
|
8
|
+
import { EMPTY_OBJ } from '../../constants'
|
|
9
|
+
import type { ElevationProps } from './elevation.types'
|
|
10
|
+
|
|
11
|
+
import $elevation from './elevation.module.css'
|
|
12
|
+
|
|
13
|
+
function elevationClasses(p: ElevationProps, s?: CSSModuleClassList): ClassList {
|
|
14
|
+
const { elevation } = p
|
|
15
|
+
if (elevation === undefined) return []
|
|
16
|
+
const $ = mergeCssModulesStyles($elevation, s)
|
|
17
|
+
|
|
18
|
+
const value = Number(elevation)
|
|
19
|
+
|
|
20
|
+
return Number.isNaN(value) ? [] : [$['elv' + Math.abs(value) + (value < 0 ? 'n' : '')], $.elv]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function elevationDeal(instance: ContractProvider): ThemeContractDeal {
|
|
24
|
+
const props = instance.props
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
classes: () => {
|
|
28
|
+
if (instance.context) {
|
|
29
|
+
if (props.elevation === 'inherit' && instance.context.elevation) {
|
|
30
|
+
return instance.inject(elevationClasses({ elevation: instance.context.elevation.value }, instance.$styles))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return instance.inject(elevationClasses(props, instance.$styles))
|
|
34
|
+
},
|
|
35
|
+
styles: () => instance.inject(EMPTY_OBJ)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The elevation contract's generator endpoint: ONE .elv base class carries the
|
|
2
|
+
// box-shadow (built from css vars), the per-step classes (.elv1..steps /
|
|
3
|
+
// .elv1n..stepsn for inset) only fill in the var values for their step - no
|
|
4
|
+
// shadow math is repeated per class. Shadow tuning (color, steps) lands in
|
|
5
|
+
// the config with the elevation appearance settings.
|
|
6
|
+
import type { CSSModuleClassList } from '@ui0/types'
|
|
7
|
+
import type { ThemeContractGenerator } from '@ui0/types'
|
|
8
|
+
|
|
9
|
+
import type { ContractFactory } from '../generator'
|
|
10
|
+
import type { ElevationThemeAppearanceConfig } from './elevation.types'
|
|
11
|
+
|
|
12
|
+
import $elevation from './elevation.module.css'
|
|
13
|
+
|
|
14
|
+
// the shared shadow: a fixed highlight ring + three layers assembled from the
|
|
15
|
+
// --0-elv-* vars (the step classes fill those in; the base class shadows with
|
|
16
|
+
// zero/fallback values until one is applied)
|
|
17
|
+
const BASE_CLASS_BODY = ` box-shadow:
|
|
18
|
+
0 0 0 1px rgba(255 255 255 / .1),
|
|
19
|
+
var(--0-elv-i, ) 0px 1px 1px rgba(var(--0-elv-c, 0 0 0) / 0.1),
|
|
20
|
+
var(--0-elv-i, ) var(--0-elv-x1, 0px) var(--0-elv-y1, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.095),
|
|
21
|
+
var(--0-elv-i, ) var(--0-elv-x2, 0px) var(--0-elv-y2, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.13);`
|
|
22
|
+
|
|
23
|
+
const elevationGenerator: ContractFactory = (ctx): ThemeContractGenerator => {
|
|
24
|
+
const { config, hash } = ctx
|
|
25
|
+
const steps = (config as { elevation?: ElevationThemeAppearanceConfig }).elevation?.steps ?? 16
|
|
26
|
+
function vars() {
|
|
27
|
+
return {
|
|
28
|
+
elv: {
|
|
29
|
+
x1: '1px',
|
|
30
|
+
y1: 'calc(var(--0-elv-x1) * 3)',
|
|
31
|
+
x2: 'calc(var(--0-elv-x1) * 1.5)',
|
|
32
|
+
y2: 'calc(var(--0-elv-x1) * 1.5)'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function stepVars(i: number, inset: boolean): string {
|
|
37
|
+
const lines = [` --0-elv-x1: ${i}px;`]
|
|
38
|
+
if (inset) lines.push(' --0-elv-i: inset;')
|
|
39
|
+
return lines.join('\n')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function css(): string {
|
|
43
|
+
const lines: string[] = [`.${$elevation.elv} {\n${BASE_CLASS_BODY}\n}`]
|
|
44
|
+
for (let i = 1; i <= steps; i++) {
|
|
45
|
+
lines.push(`.${hash(`elv${i}`)} {\n${stepVars(i, false)}\n}`)
|
|
46
|
+
lines.push(`.${hash(`elv${i}n`)} {\n${stepVars(i, true)}\n}`)
|
|
47
|
+
}
|
|
48
|
+
return lines.join('\n')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function classes(): CSSModuleClassList {
|
|
52
|
+
// the base class + the default 16 steps come from the registry map; a
|
|
53
|
+
// config that extends the step count generates the extras dynamically
|
|
54
|
+
const out: CSSModuleClassList = { ...$elevation }
|
|
55
|
+
for (let i = 1; i <= steps; i++) {
|
|
56
|
+
out[`elv${i}`] = hash(`elv${i}`)
|
|
57
|
+
out[`elv${i}n`] = hash(`elv${i}n`)
|
|
58
|
+
}
|
|
59
|
+
return out
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
namespace: 'e',
|
|
64
|
+
vars,
|
|
65
|
+
css,
|
|
66
|
+
class: classes,
|
|
67
|
+
styles: () => ({})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default elevationGenerator
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ComponentElevation } from '@ui0/types'
|
|
2
|
+
import type { ThemeContractDeal } from '@ui0/types'
|
|
3
|
+
import type { InjectionKey, Ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
export const ElevationRefKey = Symbol('ElevationRefKey') as InjectionKey<Ref<ComponentElevation>>
|
|
6
|
+
export const ElevationContractKey = Symbol('ElevationContractKey') as InjectionKey<ThemeContractDeal>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.elv,
|
|
2
|
+
.elv1,
|
|
3
|
+
.elv1n,
|
|
4
|
+
.elv2,
|
|
5
|
+
.elv2n,
|
|
6
|
+
.elv3,
|
|
7
|
+
.elv3n,
|
|
8
|
+
.elv4,
|
|
9
|
+
.elv4n,
|
|
10
|
+
.elv5,
|
|
11
|
+
.elv5n,
|
|
12
|
+
.elv6,
|
|
13
|
+
.elv6n,
|
|
14
|
+
.elv7,
|
|
15
|
+
.elv7n,
|
|
16
|
+
.elv8,
|
|
17
|
+
.elv8n,
|
|
18
|
+
.elv9,
|
|
19
|
+
.elv9n,
|
|
20
|
+
.elv10,
|
|
21
|
+
.elv10n,
|
|
22
|
+
.elv11,
|
|
23
|
+
.elv11n,
|
|
24
|
+
.elv12,
|
|
25
|
+
.elv12n,
|
|
26
|
+
.elv13,
|
|
27
|
+
.elv13n,
|
|
28
|
+
.elv14,
|
|
29
|
+
.elv14n,
|
|
30
|
+
.elv15,
|
|
31
|
+
.elv15n,
|
|
32
|
+
.elv16,
|
|
33
|
+
.elv16n {
|
|
34
|
+
/*@*/
|
|
35
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Elevation: ONE base class carries the box-shadow assembled from the
|
|
2
|
+
// --0-elv-* css vars; the per-step classes (.elv1..$steps, .elv1n..stepsn for
|
|
3
|
+
// inset) only fill in the var values for their step - no shadow math is
|
|
4
|
+
// repeated per class. This file mirrors what elevation.generator.ts emits
|
|
5
|
+
// (the generator compiles this pattern with $steps from the theme config).
|
|
6
|
+
|
|
7
|
+
// the shared shadow: a fixed highlight ring + three layers assembled from the
|
|
8
|
+
// --0-elv-* vars (zero/fallback values until a step class is applied)
|
|
9
|
+
@mixin use() {
|
|
10
|
+
box-shadow:
|
|
11
|
+
0 0 0 1px rgba(255 255 255 / 0.1),
|
|
12
|
+
var(--0-elv-i,) 0px 1px 1px rgba(var(--0-elv-c, 0 0 0) / 0.1),
|
|
13
|
+
var(--0-elv-i,) var(--0-elv-x1, 0px) var(--0-elv-y1, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.095),
|
|
14
|
+
var(--0-elv-i,) var(--0-elv-x2, 0px) var(--0-elv-y2, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.13);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@mixin elv($i, $inset: false) {
|
|
18
|
+
--0-elv-x1: #{$i}px;
|
|
19
|
+
--0-elv-y1: #{3 * $i}px;
|
|
20
|
+
--0-elv-x2: #{1.5 * $i}px;
|
|
21
|
+
--0-elv-y2: #{1.5 * $i}px;
|
|
22
|
+
@if $inset {
|
|
23
|
+
--0-elv-i: inset;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@mixin elevator($elevation, $color-value: #000) {
|
|
28
|
+
// legacy helper - kept for hand-written component styles; the generated
|
|
29
|
+
// classes use the var pattern above (@include use + @include elv)
|
|
30
|
+
$inset: 0;
|
|
31
|
+
@if $elevation < 0 {
|
|
32
|
+
$elevation: $elevation * -1;
|
|
33
|
+
$inset: inset 0;
|
|
34
|
+
} @else {
|
|
35
|
+
$inset: 0;
|
|
36
|
+
}
|
|
37
|
+
box-shadow:
|
|
38
|
+
0 0 0 1px rgba(255, 255, 255, 0.1),
|
|
39
|
+
$inset 0px 1px 1px rgba($color-value, 0.1),
|
|
40
|
+
$inset #{1 * $elevation}px #{3 * $elevation}px rgba($color-value, 0.095),
|
|
41
|
+
$inset #{1.5 * $elevation}px #{1.5 * $elevation}px rgba($color-value, 0.13);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@function stroke() {
|
|
45
|
+
@return 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
46
|
+
}
|