@purestack/ts-components 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -0
- package/dist/ts-components.d.mts +1662 -0
- package/dist/ts-components.d.mts.map +1 -0
- package/dist/ts-components.mjs +9200 -0
- package/dist/ts-components.mjs.map +1 -0
- package/package.json +130 -0
- package/src/defineComponents.ts +87 -0
- package/src/index.ts +270 -0
- package/src/registerStyles.ts +63 -0
- package/src/standard/alertBox/alertBox.ts +64 -0
- package/src/standard/autoId.ts +25 -0
- package/src/standard/badge/badge.ts +41 -0
- package/src/standard/badge/badgeStyle.ts +21 -0
- package/src/standard/barChart/barChart.ts +338 -0
- package/src/standard/barChart/barChartStyle.ts +81 -0
- package/src/standard/btn/btn.ts +191 -0
- package/src/standard/btn/btnStyle.ts +59 -0
- package/src/standard/btnGroup/btnGroup.ts +208 -0
- package/src/standard/btnGroup/btnGroupStyle.ts +85 -0
- package/src/standard/chart/cartesianChart.ts +99 -0
- package/src/standard/chart/chartDefaults.ts +25 -0
- package/src/standard/chart/chartUtils.ts +84 -0
- package/src/standard/classicLogo/classicLogo.ts +429 -0
- package/src/standard/classicLogo/classicLogoStyle.ts +292 -0
- package/src/standard/componentVariant.ts +153 -0
- package/src/standard/composer/composer.ts +1035 -0
- package/src/standard/composer/composerCanvasInitialStyle.ts +225 -0
- package/src/standard/composer/composerCanvasRevertStyle.ts +14 -0
- package/src/standard/composer/composerCanvasSharedStyle.ts +60 -0
- package/src/standard/composer/composerCanvasStyleTypes.ts +8 -0
- package/src/standard/composer/composerCanvasThemeStyle.ts +53 -0
- package/src/standard/composer/composerStyle.ts +156 -0
- package/src/standard/composer/emailHtmlPolicy.ts +474 -0
- package/src/standard/consent/consent.ts +173 -0
- package/src/standard/consent/consentStyle.ts +44 -0
- package/src/standard/contactForm/contactForm.ts +135 -0
- package/src/standard/contactForm/contactFormStyle.ts +116 -0
- package/src/standard/doughnutChart/doughnutChart.ts +527 -0
- package/src/standard/doughnutChart/doughnutChartStyle.ts +64 -0
- package/src/standard/dropFiles/dropFiles.ts +480 -0
- package/src/standard/dropFiles/dropFilesStyle.ts +171 -0
- package/src/standard/expandablePanel/expandablePanel.ts +106 -0
- package/src/standard/expandablePanel/expandablePanelStyle.ts +56 -0
- package/src/standard/flex/flex.ts +269 -0
- package/src/standard/flex/flexStyle.ts +556 -0
- package/src/standard/footer/footer.ts +93 -0
- package/src/standard/footer/footerStyle.ts +39 -0
- package/src/standard/form/autoCompleteInput.ts +567 -0
- package/src/standard/form/form.ts +205 -0
- package/src/standard/form/formCheck.ts +96 -0
- package/src/standard/form/formInputField.ts +184 -0
- package/src/standard/form/formSelectField.ts +152 -0
- package/src/standard/form/formStyle.ts +585 -0
- package/src/standard/form/multiAutoCompleteInput.ts +880 -0
- package/src/standard/grid/grid.ts +177 -0
- package/src/standard/grid/gridStyle.ts +56 -0
- package/src/standard/icon/icon.ts +96 -0
- package/src/standard/icon/iconStyle.ts +50 -0
- package/src/standard/landing/codeShowcase.ts +54 -0
- package/src/standard/landing/comparisonColumn.ts +30 -0
- package/src/standard/landing/comparisonFeature.ts +22 -0
- package/src/standard/landing/comparisonTable.ts +38 -0
- package/src/standard/landing/ctaSection.ts +59 -0
- package/src/standard/landing/featureCard.ts +41 -0
- package/src/standard/landing/landing.ts +52 -0
- package/src/standard/landing/landingBand.ts +217 -0
- package/src/standard/landing/landingSection.ts +43 -0
- package/src/standard/landing/landingTypes.ts +141 -0
- package/src/standard/landing/metricItem.ts +25 -0
- package/src/standard/landing/metricStrip.ts +20 -0
- package/src/standard/lineChart/lineChart.ts +462 -0
- package/src/standard/lineChart/lineChartStyle.ts +91 -0
- package/src/standard/logo/logo.ts +192 -0
- package/src/standard/logo/logoStyle.ts +164 -0
- package/src/standard/modal/modal.ts +171 -0
- package/src/standard/modal/modalStore.ts +154 -0
- package/src/standard/modal/modalStyle.ts +190 -0
- package/src/standard/navMenu/navMenu.ts +278 -0
- package/src/standard/navMenu/navMenuStyle.ts +323 -0
- package/src/standard/pageLinks/pageLinks.ts +90 -0
- package/src/standard/pageScript/pageScript.ts +155 -0
- package/src/standard/pageToc/pageToc.ts +154 -0
- package/src/standard/pageToc/pageTocStyle.ts +372 -0
- package/src/standard/panel/panel.ts +55 -0
- package/src/standard/panel/panelStyle.ts +15 -0
- package/src/standard/pricing/pricing.ts +146 -0
- package/src/standard/searchBox/searchBox.ts +54 -0
- package/src/standard/searchBox/searchBoxStyle.ts +180 -0
- package/src/standard/sectionHeader/sectionHeader.ts +99 -0
- package/src/standard/signIn/signIn.ts +167 -0
- package/src/standard/signIn/signInStyle.ts +70 -0
- package/src/standard/tabs/tabs.ts +193 -0
- package/src/standard/tabs/tabsStyle.ts +231 -0
- package/src/standard/themeToggle/themeToggle.ts +33 -0
- package/src/standard/themeToggle/themeToggleStyle.ts +88 -0
- package/src/standard/toast/toastHost.ts +42 -0
- package/src/standard/toast/toastStore.ts +50 -0
- package/src/standard/topBar/topBar.ts +101 -0
- package/src/standard/topBar/topBarStyle.ts +169 -0
- package/src/standard/virtualList/variableVirtualTable.ts +371 -0
- package/src/standard/virtualList/virtualList.ts +423 -0
- package/src/standard/virtualList/virtualListStyle.ts +34 -0
- package/src/standard/virtualList/virtualTable.ts +179 -0
- package/src/standard/virtualList/virtualTableStyle.ts +24 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ThemePalette } from '@purestack/ts-style'
|
|
2
|
+
import { styleBuilder, type ThemeMode, themes } from '@purestack/ts-style'
|
|
3
|
+
|
|
4
|
+
export function registerButtonStyles() {
|
|
5
|
+
themes.forEach((theme, palette) => {
|
|
6
|
+
registerButtonBaseStyles(theme, palette)
|
|
7
|
+
registerButtonSizeStyles(theme, palette)
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function registerButtonBaseStyles(theme: ThemeMode, palette: ThemePalette) {
|
|
12
|
+
styleBuilder
|
|
13
|
+
.select('.btn', theme)
|
|
14
|
+
.display('inline-flex')
|
|
15
|
+
.background('transparent')
|
|
16
|
+
.verticalAlign('middle')
|
|
17
|
+
.alignItems('center')
|
|
18
|
+
.justifyContent('center')
|
|
19
|
+
.gap('0.25em')
|
|
20
|
+
.border('none')
|
|
21
|
+
.borderColor('transparent')
|
|
22
|
+
.cursor('pointer')
|
|
23
|
+
.textDecoration('none')
|
|
24
|
+
.whiteSpace('nowrap')
|
|
25
|
+
.transition(
|
|
26
|
+
'background 150ms ease, color 150ms ease, border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease',
|
|
27
|
+
)
|
|
28
|
+
.webkitTapHighlightColor('transparent')
|
|
29
|
+
.padding('0.5em 0.75em')
|
|
30
|
+
.apply(palette.applyFont(palette.font.size.body, palette.font.weight.w500))
|
|
31
|
+
.userSelect('none')
|
|
32
|
+
|
|
33
|
+
styleBuilder
|
|
34
|
+
.select('.btn:disabled', theme)
|
|
35
|
+
.cursor('not-allowed')
|
|
36
|
+
.transform('none')
|
|
37
|
+
.boxShadow('none')
|
|
38
|
+
|
|
39
|
+
styleBuilder
|
|
40
|
+
.select('.btn__label', theme)
|
|
41
|
+
.display('inline-flex')
|
|
42
|
+
.alignItems('center')
|
|
43
|
+
|
|
44
|
+
styleBuilder.select('.btn__icon', theme).width('1.1em').height('1.1em')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function registerButtonSizeStyles(theme: ThemeMode, palette: ThemePalette) {
|
|
48
|
+
styleBuilder
|
|
49
|
+
.select('.btn--sm', theme)
|
|
50
|
+
.apply(palette.applyFont(palette.font.size.xs, palette.font.weight.w500))
|
|
51
|
+
|
|
52
|
+
/*- The default size (md) is defined in the .btn for simplicity. */
|
|
53
|
+
|
|
54
|
+
styleBuilder
|
|
55
|
+
.select('.btn--lg', theme)
|
|
56
|
+
.apply(palette.applyFont(palette.font.size.h2, palette.font.weight.w500))
|
|
57
|
+
styleBuilder.select('.btn--icon-only', theme).padding('0.25em')
|
|
58
|
+
styleBuilder.select('.btn--icon-only .btn__label', theme).display('none')
|
|
59
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { SemanticTone } from '@purestack/ts-style'
|
|
2
|
+
import {
|
|
3
|
+
type ComputedRef,
|
|
4
|
+
computed,
|
|
5
|
+
defineComponent,
|
|
6
|
+
html,
|
|
7
|
+
type RefOrValue,
|
|
8
|
+
unref,
|
|
9
|
+
} from 'regor'
|
|
10
|
+
import type { BtnIconPosition, BtnSize } from '../btn/btn'
|
|
11
|
+
import {
|
|
12
|
+
type ComponentVariant,
|
|
13
|
+
type ComponentVariantMode,
|
|
14
|
+
resolveComponentClasses,
|
|
15
|
+
} from '../componentVariant'
|
|
16
|
+
|
|
17
|
+
export type BtnGroupAlign = 'start' | 'center' | 'end'
|
|
18
|
+
export type BtnGroupDropDownAlign = 'start' | 'end'
|
|
19
|
+
|
|
20
|
+
export interface BtnGroup {
|
|
21
|
+
align?: RefOrValue<BtnGroupAlign>
|
|
22
|
+
wrap?: RefOrValue<boolean>
|
|
23
|
+
classes?: ComputedRef<string>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface BtnGroupDropDown {
|
|
27
|
+
label?: RefOrValue<string>
|
|
28
|
+
icon?: RefOrValue<string>
|
|
29
|
+
iconPosition?: RefOrValue<BtnIconPosition>
|
|
30
|
+
iconOnly?: RefOrValue<boolean>
|
|
31
|
+
ariaLabel?: RefOrValue<string>
|
|
32
|
+
tone?: RefOrValue<SemanticTone>
|
|
33
|
+
size?: RefOrValue<BtnSize>
|
|
34
|
+
variant?: RefOrValue<ComponentVariant>
|
|
35
|
+
variantMode?: RefOrValue<ComponentVariantMode>
|
|
36
|
+
menuTone?: RefOrValue<SemanticTone>
|
|
37
|
+
menuVariant?: RefOrValue<ComponentVariant>
|
|
38
|
+
menuVariantMode?: RefOrValue<ComponentVariantMode>
|
|
39
|
+
align?: RefOrValue<BtnGroupDropDownAlign>
|
|
40
|
+
classes?: ComputedRef<string>
|
|
41
|
+
triggerClasses?: ComputedRef<string>
|
|
42
|
+
menuClasses?: ComputedRef<string>
|
|
43
|
+
resolvedAlign?: ComputedRef<BtnGroupDropDownAlign>
|
|
44
|
+
resolvedLabel?: ComputedRef<string>
|
|
45
|
+
resolvedIcon?: ComputedRef<string>
|
|
46
|
+
resolvedAriaLabel?: ComputedRef<string | undefined>
|
|
47
|
+
showStartIcon?: ComputedRef<boolean>
|
|
48
|
+
showEndIcon?: ComputedRef<boolean>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const DEFAULT_BUTTON_VARIANT: ComponentVariant = 'solid'
|
|
52
|
+
const DEFAULT_DROPDOWN_LABEL = 'More'
|
|
53
|
+
const DEFAULT_DROPDOWN_ICON = 'lucide:chevron-down'
|
|
54
|
+
|
|
55
|
+
const btnGroupTemplate = html`<div class="btn-group" :class="classes">
|
|
56
|
+
<slot></slot>
|
|
57
|
+
</div>`
|
|
58
|
+
|
|
59
|
+
const btnGroupDropDownTemplate = html`<details
|
|
60
|
+
class="btn-group__dropdown"
|
|
61
|
+
:class="classes"
|
|
62
|
+
:data-menu-align="resolvedAlign"
|
|
63
|
+
data-menu-runtime
|
|
64
|
+
>
|
|
65
|
+
<summary
|
|
66
|
+
class="btn btn-group__toggle"
|
|
67
|
+
:class="triggerClasses"
|
|
68
|
+
:aria-label="resolvedAriaLabel"
|
|
69
|
+
>
|
|
70
|
+
<Icon class="btn__icon" :name="resolvedIcon" r-if="showStartIcon"/>
|
|
71
|
+
<span class="btn__label" r-if="!iconOnly">{{ resolvedLabel }}</span>
|
|
72
|
+
<Icon class="btn__icon" :name="resolvedIcon" r-if="showEndIcon"/>
|
|
73
|
+
</summary>
|
|
74
|
+
<div class="btn-group__menu" :class="menuClasses" data-menu-panel>
|
|
75
|
+
<slot></slot>
|
|
76
|
+
</div>
|
|
77
|
+
</details>`
|
|
78
|
+
|
|
79
|
+
function defineBtnGroupComponent() {
|
|
80
|
+
return defineComponent<BtnGroup>(btnGroupTemplate, {
|
|
81
|
+
props: ['align', 'wrap'],
|
|
82
|
+
context: (head) => resolveBtnGroup(head.props),
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function defineBtnGroupDropDownComponent() {
|
|
87
|
+
return defineComponent<BtnGroupDropDown>(btnGroupDropDownTemplate, {
|
|
88
|
+
props: [
|
|
89
|
+
'label',
|
|
90
|
+
'icon',
|
|
91
|
+
'iconPosition',
|
|
92
|
+
'iconOnly',
|
|
93
|
+
'ariaLabel',
|
|
94
|
+
'tone',
|
|
95
|
+
'size',
|
|
96
|
+
'variant',
|
|
97
|
+
'variantMode',
|
|
98
|
+
'menuTone',
|
|
99
|
+
'menuVariant',
|
|
100
|
+
'menuVariantMode',
|
|
101
|
+
'align',
|
|
102
|
+
],
|
|
103
|
+
context: (head) => resolveBtnGroupDropDown(head.props),
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function defineBtnGroupComponents() {
|
|
108
|
+
return {
|
|
109
|
+
btnGroup: defineBtnGroupComponent(),
|
|
110
|
+
btnGroupDropDown: defineBtnGroupDropDownComponent(),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function resolveBtnGroup(props: BtnGroup): BtnGroup {
|
|
115
|
+
return {
|
|
116
|
+
...props,
|
|
117
|
+
classes: computed(() =>
|
|
118
|
+
[
|
|
119
|
+
resolveBtnGroupAlignClass(props),
|
|
120
|
+
unref(props.wrap) ? 'btn-group--wrap' : '',
|
|
121
|
+
]
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
.join(' '),
|
|
124
|
+
),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveBtnGroupDropDown(props: BtnGroupDropDown): BtnGroupDropDown {
|
|
129
|
+
return {
|
|
130
|
+
...props,
|
|
131
|
+
classes: computed(() => resolveDropDownClasses(props)),
|
|
132
|
+
triggerClasses: computed(() =>
|
|
133
|
+
resolveComponentClasses(props, {
|
|
134
|
+
defaultVariant: DEFAULT_BUTTON_VARIANT,
|
|
135
|
+
classes: resolveButtonStateClasses(props),
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
menuClasses: computed(() =>
|
|
139
|
+
resolveComponentClasses(
|
|
140
|
+
{
|
|
141
|
+
tone: props.menuTone,
|
|
142
|
+
variant: props.menuVariant,
|
|
143
|
+
variantMode: props.menuVariantMode,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
defaultVariant: 'surfaceAlt',
|
|
147
|
+
defaultVariantMode: 'stateless',
|
|
148
|
+
},
|
|
149
|
+
),
|
|
150
|
+
),
|
|
151
|
+
resolvedAlign: computed(() => resolveDropDownAlign(props)),
|
|
152
|
+
resolvedLabel: computed(() => unref(props.label) || DEFAULT_DROPDOWN_LABEL),
|
|
153
|
+
resolvedIcon: computed(() => unref(props.icon) || DEFAULT_DROPDOWN_ICON),
|
|
154
|
+
resolvedAriaLabel: computed(() => resolveDropDownAriaLabel(props)),
|
|
155
|
+
showStartIcon: computed(() => resolveShowStartIcon(props)),
|
|
156
|
+
showEndIcon: computed(() => resolveShowEndIcon(props)),
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function resolveBtnGroupAlignClass(props: BtnGroup) {
|
|
161
|
+
const align = unref(props.align)
|
|
162
|
+
return align ? `btn-group--${align}` : ''
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function resolveDropDownClasses(props: BtnGroupDropDown) {
|
|
166
|
+
return `btn-group__dropdown--${resolveDropDownAlign(props)}`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function resolveDropDownAlign(props: BtnGroupDropDown): BtnGroupDropDownAlign {
|
|
170
|
+
return unref(props.align) || 'end'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function resolveButtonStateClasses(props: BtnGroupDropDown) {
|
|
174
|
+
const size = unref(props.size)
|
|
175
|
+
const hasIcon = !!resolveIconName(props)
|
|
176
|
+
const iconPosition = unref(props.iconPosition)
|
|
177
|
+
const iconOnly = unref(props.iconOnly)
|
|
178
|
+
const classes = [size ? `btn--${size}` : '']
|
|
179
|
+
if (!hasIcon) return classes
|
|
180
|
+
classes.push(
|
|
181
|
+
iconOnly
|
|
182
|
+
? 'btn--icon-only'
|
|
183
|
+
: iconPosition === 'start'
|
|
184
|
+
? 'btn--icon-start'
|
|
185
|
+
: 'btn--icon-end',
|
|
186
|
+
)
|
|
187
|
+
return classes
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function resolveShowStartIcon(props: BtnGroupDropDown) {
|
|
191
|
+
return !!resolveIconName(props) && unref(props.iconPosition) === 'start'
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function resolveShowEndIcon(props: BtnGroupDropDown) {
|
|
195
|
+
return !!resolveIconName(props) && unref(props.iconPosition) !== 'start'
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function resolveIconName(props: BtnGroupDropDown) {
|
|
199
|
+
return unref(props.icon) || DEFAULT_DROPDOWN_ICON
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function resolveDropDownAriaLabel(props: BtnGroupDropDown) {
|
|
203
|
+
const ariaLabel = unref(props.ariaLabel)
|
|
204
|
+
if (ariaLabel) return ariaLabel
|
|
205
|
+
return unref(props.iconOnly)
|
|
206
|
+
? unref(props.label) || 'More actions'
|
|
207
|
+
: undefined
|
|
208
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ThemePalette } from '@purestack/ts-style'
|
|
2
|
+
import { styleBuilder, type ThemeMode, themes } from '@purestack/ts-style'
|
|
3
|
+
|
|
4
|
+
export function registerBtnGroupStyles() {
|
|
5
|
+
themes.forEach((theme, palette) => {
|
|
6
|
+
registerBtnGroupLayoutStyles(theme)
|
|
7
|
+
registerBtnGroupDropDownStyles(theme, palette)
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function registerBtnGroupLayoutStyles(theme: ThemeMode) {
|
|
12
|
+
styleBuilder
|
|
13
|
+
.select('.btn-group', theme)
|
|
14
|
+
.display('inline-flex')
|
|
15
|
+
.alignItems('center')
|
|
16
|
+
.justifyContent('flex-start')
|
|
17
|
+
.gap('0.375rem')
|
|
18
|
+
.verticalAlign('middle')
|
|
19
|
+
|
|
20
|
+
styleBuilder.select('.btn-group--center', theme).justifyContent('center')
|
|
21
|
+
styleBuilder.select('.btn-group--end', theme).justifyContent('flex-end')
|
|
22
|
+
styleBuilder.select('.btn-group--wrap', theme).flexWrap('wrap')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function registerBtnGroupDropDownStyles(
|
|
26
|
+
theme: ThemeMode,
|
|
27
|
+
palette: ThemePalette,
|
|
28
|
+
) {
|
|
29
|
+
styleBuilder
|
|
30
|
+
.select('.btn-group__dropdown', theme)
|
|
31
|
+
.position('relative')
|
|
32
|
+
.display('inline-flex')
|
|
33
|
+
.lineHeight('normal')
|
|
34
|
+
|
|
35
|
+
styleBuilder.select('.btn-group__toggle', theme).listStyle('none')
|
|
36
|
+
styleBuilder.select('.btn-group__toggle::marker', theme).content('""')
|
|
37
|
+
styleBuilder
|
|
38
|
+
.select('.btn-group__toggle::-webkit-details-marker', theme)
|
|
39
|
+
.display('none')
|
|
40
|
+
|
|
41
|
+
styleBuilder
|
|
42
|
+
.select('.btn-group__dropdown[open] > .btn-group__toggle', theme)
|
|
43
|
+
.boxShadow(`0 0 0 3px ${palette.current.border.focus}`)
|
|
44
|
+
|
|
45
|
+
styleBuilder
|
|
46
|
+
.select('.btn-group__menu', theme)
|
|
47
|
+
.position('absolute')
|
|
48
|
+
.display('none')
|
|
49
|
+
.top('calc(100% + 0.375rem)')
|
|
50
|
+
.right('0')
|
|
51
|
+
.zIndex(70)
|
|
52
|
+
.width('max-content')
|
|
53
|
+
.minWidth('max(11rem, 100%)')
|
|
54
|
+
.maxWidth('calc(100vw - 2rem)')
|
|
55
|
+
.padding('0.375rem')
|
|
56
|
+
.gap('0.25rem')
|
|
57
|
+
.boxShadow(palette.effect.strongShadow)
|
|
58
|
+
|
|
59
|
+
styleBuilder
|
|
60
|
+
.select('.btn-group__dropdown--start > .btn-group__menu', theme)
|
|
61
|
+
.left('0')
|
|
62
|
+
.right('auto')
|
|
63
|
+
|
|
64
|
+
styleBuilder
|
|
65
|
+
.select('.btn-group__dropdown[open] > .btn-group__menu', theme)
|
|
66
|
+
.display('grid')
|
|
67
|
+
|
|
68
|
+
styleBuilder
|
|
69
|
+
.select('.btn-group__menu > .btn', theme)
|
|
70
|
+
.width('100%')
|
|
71
|
+
.justifyContent('flex-start')
|
|
72
|
+
.textAlign('left')
|
|
73
|
+
.whiteSpace('normal')
|
|
74
|
+
|
|
75
|
+
styleBuilder
|
|
76
|
+
.select('.btn-group__menu > .btn .btn__label', theme)
|
|
77
|
+
.minWidth('0')
|
|
78
|
+
.whiteSpace('normal')
|
|
79
|
+
.overflowWrap('anywhere')
|
|
80
|
+
|
|
81
|
+
styleBuilder
|
|
82
|
+
.select('.btn-group__menu > *', theme)
|
|
83
|
+
.maxWidth('100%')
|
|
84
|
+
.minWidth('0')
|
|
85
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { RefOrValue } from 'regor'
|
|
2
|
+
import {
|
|
3
|
+
CARTESIAN_CHART_PLOT_HEIGHT,
|
|
4
|
+
CARTESIAN_CHART_PLOT_WIDTH,
|
|
5
|
+
CARTESIAN_CHART_PLOT_X,
|
|
6
|
+
CARTESIAN_CHART_PLOT_Y,
|
|
7
|
+
DEFAULT_CARTESIAN_GRID_LINE_COUNT,
|
|
8
|
+
} from './chartDefaults'
|
|
9
|
+
import {
|
|
10
|
+
formatChartValue,
|
|
11
|
+
readChartNumber,
|
|
12
|
+
resolveChartText,
|
|
13
|
+
} from './chartUtils'
|
|
14
|
+
|
|
15
|
+
export interface CartesianChartDomain {
|
|
16
|
+
min: number
|
|
17
|
+
max: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ResolvedCartesianChartGridLine {
|
|
21
|
+
value: number
|
|
22
|
+
formattedValue: string
|
|
23
|
+
y: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ResolveCartesianChartDomainOptions {
|
|
27
|
+
values: number[]
|
|
28
|
+
minValue?: RefOrValue<number | string>
|
|
29
|
+
maxValue?: RefOrValue<number | string>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveCartesianChartDomain({
|
|
33
|
+
values,
|
|
34
|
+
minValue,
|
|
35
|
+
maxValue,
|
|
36
|
+
}: ResolveCartesianChartDomainOptions): CartesianChartDomain {
|
|
37
|
+
const fallbackMax = values.length ? Math.max(...values) : 1
|
|
38
|
+
const fallbackMin = values.length ? Math.min(...values) : 0
|
|
39
|
+
let min = Math.min(
|
|
40
|
+
readChartNumber(minValue, Math.min(0, fallbackMin)),
|
|
41
|
+
fallbackMin,
|
|
42
|
+
0,
|
|
43
|
+
)
|
|
44
|
+
let max = Math.max(
|
|
45
|
+
readChartNumber(maxValue, Math.max(0, fallbackMax)),
|
|
46
|
+
fallbackMax,
|
|
47
|
+
0,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if (min === max) {
|
|
51
|
+
if (min === 0) max = 1
|
|
52
|
+
else if (min > 0) min = 0
|
|
53
|
+
else max = 0
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (min > max) [min, max] = [max, min]
|
|
57
|
+
|
|
58
|
+
return { min, max }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function resolveCartesianChartGridLines(
|
|
62
|
+
domain: CartesianChartDomain,
|
|
63
|
+
valueSuffix?: RefOrValue<string>,
|
|
64
|
+
): ResolvedCartesianChartGridLine[] {
|
|
65
|
+
const suffix = resolveChartText(valueSuffix)
|
|
66
|
+
const step =
|
|
67
|
+
(domain.max - domain.min) / (DEFAULT_CARTESIAN_GRID_LINE_COUNT - 1)
|
|
68
|
+
|
|
69
|
+
return Array.from(
|
|
70
|
+
{ length: DEFAULT_CARTESIAN_GRID_LINE_COUNT },
|
|
71
|
+
(_, index) => {
|
|
72
|
+
const value = domain.max - step * index
|
|
73
|
+
return {
|
|
74
|
+
value,
|
|
75
|
+
formattedValue: formatChartValue(value, suffix),
|
|
76
|
+
y: valueToCartesianChartY(value, domain),
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function valueToCartesianChartY(
|
|
83
|
+
value: number,
|
|
84
|
+
domain: CartesianChartDomain,
|
|
85
|
+
) {
|
|
86
|
+
const range = domain.max - domain.min
|
|
87
|
+
if (range <= 0) return CARTESIAN_CHART_PLOT_Y + CARTESIAN_CHART_PLOT_HEIGHT
|
|
88
|
+
const ratio = (domain.max - value) / range
|
|
89
|
+
return CARTESIAN_CHART_PLOT_Y + ratio * CARTESIAN_CHART_PLOT_HEIGHT
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function pointIndexToCartesianChartX(index: number, count: number) {
|
|
93
|
+
if (count <= 1) {
|
|
94
|
+
return CARTESIAN_CHART_PLOT_X + CARTESIAN_CHART_PLOT_WIDTH / 2
|
|
95
|
+
}
|
|
96
|
+
return (
|
|
97
|
+
CARTESIAN_CHART_PLOT_X + (index / (count - 1)) * CARTESIAN_CHART_PLOT_WIDTH
|
|
98
|
+
)
|
|
99
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getThemePaletteVar } from '@purestack/ts-style'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_CHART_SIZE = '100%'
|
|
4
|
+
export const DEFAULT_CARTESIAN_CHART_WIDTH = '100%'
|
|
5
|
+
export const DEFAULT_CARTESIAN_CHART_HEIGHT = undefined
|
|
6
|
+
export const DEFAULT_CHART_EMPTY_LABEL = 'No data'
|
|
7
|
+
export const DEFAULT_CARTESIAN_GRID_LINE_COUNT = 5
|
|
8
|
+
|
|
9
|
+
export const CARTESIAN_CHART_VIEWBOX_HEIGHT = 58
|
|
10
|
+
export const CARTESIAN_CHART_PLOT_X = 8
|
|
11
|
+
export const CARTESIAN_CHART_PLOT_Y = 6
|
|
12
|
+
export const CARTESIAN_CHART_PLOT_WIDTH = 86
|
|
13
|
+
export const CARTESIAN_CHART_PLOT_HEIGHT = 43
|
|
14
|
+
export const CARTESIAN_CHART_LABEL_Y = 53.5
|
|
15
|
+
|
|
16
|
+
export const DEFAULT_CHART_COLORS = [
|
|
17
|
+
getThemePaletteVar('semanticTone.accent.button.hover.bgcolor'),
|
|
18
|
+
getThemePaletteVar('semanticTone.feature.button.hover.bgcolor'),
|
|
19
|
+
getThemePaletteVar('semanticTone.success.button.hover.bgcolor'),
|
|
20
|
+
getThemePaletteVar('semanticTone.info.button.hover.bgcolor'),
|
|
21
|
+
getThemePaletteVar('semanticTone.warning.button.hover.bgcolor'),
|
|
22
|
+
getThemePaletteVar('semanticTone.secondary.button.hover.bgcolor'),
|
|
23
|
+
getThemePaletteVar('semanticTone.danger.button.hover.bgcolor'),
|
|
24
|
+
getThemePaletteVar('semanticTone.custom.button.hover.bgcolor'),
|
|
25
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { clamp } from '@purestack/ts-util'
|
|
2
|
+
import { type RefOrValue, unref } from 'regor'
|
|
3
|
+
|
|
4
|
+
export interface FormatChartNumberOptions {
|
|
5
|
+
precision?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function readChartNumber(
|
|
9
|
+
value: RefOrValue<number | string> | undefined,
|
|
10
|
+
fallback: number,
|
|
11
|
+
) {
|
|
12
|
+
const resolved = unref(value)
|
|
13
|
+
const number =
|
|
14
|
+
typeof resolved === 'number'
|
|
15
|
+
? resolved
|
|
16
|
+
: Number.parseFloat(String(resolved))
|
|
17
|
+
return Number.isFinite(number) ? number : fallback
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function clampChartNumber(
|
|
21
|
+
value: RefOrValue<number | string> | undefined,
|
|
22
|
+
fallback: number,
|
|
23
|
+
min: number,
|
|
24
|
+
max: number,
|
|
25
|
+
) {
|
|
26
|
+
return clamp(readChartNumber(value, fallback), min, max)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveChartText(value?: RefOrValue<string>) {
|
|
30
|
+
const resolved = unref(value)
|
|
31
|
+
return typeof resolved === 'string' ? resolved.trim() : ''
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function resolveChartSize(
|
|
35
|
+
size: RefOrValue<number | string> | undefined,
|
|
36
|
+
fallback: number | string | undefined,
|
|
37
|
+
) {
|
|
38
|
+
const resolved = unref(size)
|
|
39
|
+
if (typeof resolved === 'number' && Number.isFinite(resolved)) {
|
|
40
|
+
return Math.max(1, resolved)
|
|
41
|
+
}
|
|
42
|
+
if (typeof resolved !== 'string') return fallback
|
|
43
|
+
const trimmed = resolved.trim()
|
|
44
|
+
return trimmed || fallback
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function resolveChartCssSize(size?: RefOrValue<number | string>) {
|
|
48
|
+
const resolved = unref(size)
|
|
49
|
+
if (typeof resolved === 'number' && Number.isFinite(resolved)) {
|
|
50
|
+
return `${resolved}px`
|
|
51
|
+
}
|
|
52
|
+
if (typeof resolved !== 'string') return ''
|
|
53
|
+
const trimmed = resolved.trim()
|
|
54
|
+
return isNumericChartString(trimmed) ? `${trimmed}px` : trimmed
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function formatChartValue(
|
|
58
|
+
value: number,
|
|
59
|
+
suffix: string,
|
|
60
|
+
options: FormatChartNumberOptions = {},
|
|
61
|
+
) {
|
|
62
|
+
const formatted = Number.isInteger(value)
|
|
63
|
+
? String(value)
|
|
64
|
+
: formatChartNumber(value, options)
|
|
65
|
+
return suffix ? `${formatted}${suffix}` : formatted
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function formatChartPoint(
|
|
69
|
+
point: { x: number; y: number },
|
|
70
|
+
options: FormatChartNumberOptions = {},
|
|
71
|
+
) {
|
|
72
|
+
return `${formatChartNumber(point.x, options)} ${formatChartNumber(point.y, options)}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function formatChartNumber(
|
|
76
|
+
value: number,
|
|
77
|
+
{ precision = 2 }: FormatChartNumberOptions = {},
|
|
78
|
+
) {
|
|
79
|
+
return Number.parseFloat(value.toFixed(precision)).toString()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isNumericChartString(value: string) {
|
|
83
|
+
return /^-?\d+(?:\.\d+)?$/.test(value)
|
|
84
|
+
}
|