@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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -0
  3. package/dist/ts-components.d.mts +1662 -0
  4. package/dist/ts-components.d.mts.map +1 -0
  5. package/dist/ts-components.mjs +9200 -0
  6. package/dist/ts-components.mjs.map +1 -0
  7. package/package.json +130 -0
  8. package/src/defineComponents.ts +87 -0
  9. package/src/index.ts +270 -0
  10. package/src/registerStyles.ts +63 -0
  11. package/src/standard/alertBox/alertBox.ts +64 -0
  12. package/src/standard/autoId.ts +25 -0
  13. package/src/standard/badge/badge.ts +41 -0
  14. package/src/standard/badge/badgeStyle.ts +21 -0
  15. package/src/standard/barChart/barChart.ts +338 -0
  16. package/src/standard/barChart/barChartStyle.ts +81 -0
  17. package/src/standard/btn/btn.ts +191 -0
  18. package/src/standard/btn/btnStyle.ts +59 -0
  19. package/src/standard/btnGroup/btnGroup.ts +208 -0
  20. package/src/standard/btnGroup/btnGroupStyle.ts +85 -0
  21. package/src/standard/chart/cartesianChart.ts +99 -0
  22. package/src/standard/chart/chartDefaults.ts +25 -0
  23. package/src/standard/chart/chartUtils.ts +84 -0
  24. package/src/standard/classicLogo/classicLogo.ts +429 -0
  25. package/src/standard/classicLogo/classicLogoStyle.ts +292 -0
  26. package/src/standard/componentVariant.ts +153 -0
  27. package/src/standard/composer/composer.ts +1035 -0
  28. package/src/standard/composer/composerCanvasInitialStyle.ts +225 -0
  29. package/src/standard/composer/composerCanvasRevertStyle.ts +14 -0
  30. package/src/standard/composer/composerCanvasSharedStyle.ts +60 -0
  31. package/src/standard/composer/composerCanvasStyleTypes.ts +8 -0
  32. package/src/standard/composer/composerCanvasThemeStyle.ts +53 -0
  33. package/src/standard/composer/composerStyle.ts +156 -0
  34. package/src/standard/composer/emailHtmlPolicy.ts +474 -0
  35. package/src/standard/consent/consent.ts +173 -0
  36. package/src/standard/consent/consentStyle.ts +44 -0
  37. package/src/standard/contactForm/contactForm.ts +135 -0
  38. package/src/standard/contactForm/contactFormStyle.ts +116 -0
  39. package/src/standard/doughnutChart/doughnutChart.ts +527 -0
  40. package/src/standard/doughnutChart/doughnutChartStyle.ts +64 -0
  41. package/src/standard/dropFiles/dropFiles.ts +480 -0
  42. package/src/standard/dropFiles/dropFilesStyle.ts +171 -0
  43. package/src/standard/expandablePanel/expandablePanel.ts +106 -0
  44. package/src/standard/expandablePanel/expandablePanelStyle.ts +56 -0
  45. package/src/standard/flex/flex.ts +269 -0
  46. package/src/standard/flex/flexStyle.ts +556 -0
  47. package/src/standard/footer/footer.ts +93 -0
  48. package/src/standard/footer/footerStyle.ts +39 -0
  49. package/src/standard/form/autoCompleteInput.ts +567 -0
  50. package/src/standard/form/form.ts +205 -0
  51. package/src/standard/form/formCheck.ts +96 -0
  52. package/src/standard/form/formInputField.ts +184 -0
  53. package/src/standard/form/formSelectField.ts +152 -0
  54. package/src/standard/form/formStyle.ts +585 -0
  55. package/src/standard/form/multiAutoCompleteInput.ts +880 -0
  56. package/src/standard/grid/grid.ts +177 -0
  57. package/src/standard/grid/gridStyle.ts +56 -0
  58. package/src/standard/icon/icon.ts +96 -0
  59. package/src/standard/icon/iconStyle.ts +50 -0
  60. package/src/standard/landing/codeShowcase.ts +54 -0
  61. package/src/standard/landing/comparisonColumn.ts +30 -0
  62. package/src/standard/landing/comparisonFeature.ts +22 -0
  63. package/src/standard/landing/comparisonTable.ts +38 -0
  64. package/src/standard/landing/ctaSection.ts +59 -0
  65. package/src/standard/landing/featureCard.ts +41 -0
  66. package/src/standard/landing/landing.ts +52 -0
  67. package/src/standard/landing/landingBand.ts +217 -0
  68. package/src/standard/landing/landingSection.ts +43 -0
  69. package/src/standard/landing/landingTypes.ts +141 -0
  70. package/src/standard/landing/metricItem.ts +25 -0
  71. package/src/standard/landing/metricStrip.ts +20 -0
  72. package/src/standard/lineChart/lineChart.ts +462 -0
  73. package/src/standard/lineChart/lineChartStyle.ts +91 -0
  74. package/src/standard/logo/logo.ts +192 -0
  75. package/src/standard/logo/logoStyle.ts +164 -0
  76. package/src/standard/modal/modal.ts +171 -0
  77. package/src/standard/modal/modalStore.ts +154 -0
  78. package/src/standard/modal/modalStyle.ts +190 -0
  79. package/src/standard/navMenu/navMenu.ts +278 -0
  80. package/src/standard/navMenu/navMenuStyle.ts +323 -0
  81. package/src/standard/pageLinks/pageLinks.ts +90 -0
  82. package/src/standard/pageScript/pageScript.ts +155 -0
  83. package/src/standard/pageToc/pageToc.ts +154 -0
  84. package/src/standard/pageToc/pageTocStyle.ts +372 -0
  85. package/src/standard/panel/panel.ts +55 -0
  86. package/src/standard/panel/panelStyle.ts +15 -0
  87. package/src/standard/pricing/pricing.ts +146 -0
  88. package/src/standard/searchBox/searchBox.ts +54 -0
  89. package/src/standard/searchBox/searchBoxStyle.ts +180 -0
  90. package/src/standard/sectionHeader/sectionHeader.ts +99 -0
  91. package/src/standard/signIn/signIn.ts +167 -0
  92. package/src/standard/signIn/signInStyle.ts +70 -0
  93. package/src/standard/tabs/tabs.ts +193 -0
  94. package/src/standard/tabs/tabsStyle.ts +231 -0
  95. package/src/standard/themeToggle/themeToggle.ts +33 -0
  96. package/src/standard/themeToggle/themeToggleStyle.ts +88 -0
  97. package/src/standard/toast/toastHost.ts +42 -0
  98. package/src/standard/toast/toastStore.ts +50 -0
  99. package/src/standard/topBar/topBar.ts +101 -0
  100. package/src/standard/topBar/topBarStyle.ts +169 -0
  101. package/src/standard/virtualList/variableVirtualTable.ts +371 -0
  102. package/src/standard/virtualList/virtualList.ts +423 -0
  103. package/src/standard/virtualList/virtualListStyle.ts +34 -0
  104. package/src/standard/virtualList/virtualTable.ts +179 -0
  105. package/src/standard/virtualList/virtualTableStyle.ts +24 -0
  106. 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
+ }