@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,41 @@
1
+ import type { SemanticTone } from '@purestack/ts-style'
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ defineComponent,
6
+ html,
7
+ type RefOrValue,
8
+ } from 'regor'
9
+ import {
10
+ type ComponentVariant,
11
+ resolveComponentClasses,
12
+ } from '../componentVariant'
13
+
14
+ export interface Badge {
15
+ tone?: RefOrValue<SemanticTone>
16
+ variant?: RefOrValue<ComponentVariant>
17
+ classes?: ComputedRef<string>
18
+ }
19
+
20
+ const badgeTemplate = html`<span class="badge" :class="classes"><slot></slot></span>`
21
+
22
+ function defineBadgeComponent() {
23
+ return defineComponent<Badge>(badgeTemplate, {
24
+ props: ['tone', 'variant'],
25
+ context: (head) => ({
26
+ ...head.props,
27
+ classes: computed(() =>
28
+ resolveComponentClasses(head.props, {
29
+ defaultVariant: 'surface',
30
+ defaultVariantMode: 'stateless',
31
+ }),
32
+ ),
33
+ }),
34
+ })
35
+ }
36
+
37
+ export function defineBadgeComponents() {
38
+ return {
39
+ badge: defineBadgeComponent(),
40
+ }
41
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ styleBuilder,
3
+ type ThemeMode,
4
+ type ThemePalette,
5
+ themes,
6
+ } from '@purestack/ts-style'
7
+
8
+ export function registerBadgeStyles() {
9
+ themes.forEach((theme, palette) => {
10
+ registerBadgeBaseStyles(theme, palette)
11
+ })
12
+ }
13
+
14
+ function registerBadgeBaseStyles(theme: ThemeMode, palette: ThemePalette) {
15
+ styleBuilder
16
+ .select('.badge', theme)
17
+ .display('inline-flex')
18
+ .alignItems('center')
19
+ .padding('0.12em 0.75em')
20
+ .apply(palette.applyFont(palette.font.size.xxs, palette.font.weight.w700))
21
+ }
@@ -0,0 +1,338 @@
1
+ import type { SemanticTone } from '@purestack/ts-style'
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ defineComponent,
6
+ type RefOrValue,
7
+ svg,
8
+ unref,
9
+ } from 'regor'
10
+ import {
11
+ type CartesianChartDomain,
12
+ type ResolvedCartesianChartGridLine,
13
+ resolveCartesianChartDomain,
14
+ resolveCartesianChartGridLines,
15
+ valueToCartesianChartY,
16
+ } from '../chart/cartesianChart'
17
+ import {
18
+ CARTESIAN_CHART_PLOT_WIDTH,
19
+ CARTESIAN_CHART_PLOT_X,
20
+ DEFAULT_CARTESIAN_CHART_HEIGHT,
21
+ DEFAULT_CARTESIAN_CHART_WIDTH,
22
+ DEFAULT_CHART_COLORS,
23
+ DEFAULT_CHART_EMPTY_LABEL,
24
+ } from '../chart/chartDefaults'
25
+ import {
26
+ formatChartValue,
27
+ readChartNumber,
28
+ resolveChartSize,
29
+ resolveChartText,
30
+ } from '../chart/chartUtils'
31
+ import type {
32
+ ComponentVariant,
33
+ ComponentVariantMode,
34
+ } from '../componentVariant'
35
+ import { resolveComponentClasses } from '../componentVariant'
36
+
37
+ export interface BarChartItem {
38
+ label?: RefOrValue<string>
39
+ value?: RefOrValue<number | string>
40
+ color?: RefOrValue<string>
41
+ }
42
+
43
+ export interface ResolvedBarChartItem {
44
+ label: string
45
+ value: number
46
+ formattedValue: string
47
+ color: string
48
+ x: number
49
+ y: number
50
+ width: number
51
+ height: number
52
+ valueLabelX: number
53
+ valueLabelY: number
54
+ valueLabelBaseline: string
55
+ labelX: number
56
+ labelY: number
57
+ roundedTop: number
58
+ }
59
+
60
+ export interface ResolvedBarChartGridLine
61
+ extends ResolvedCartesianChartGridLine {}
62
+
63
+ export interface BarChart {
64
+ items?: RefOrValue<Array<RefOrValue<BarChartItem>>>
65
+ title?: RefOrValue<string>
66
+ description?: RefOrValue<string>
67
+ ariaLabel?: RefOrValue<string>
68
+ emptyLabel?: RefOrValue<string>
69
+ valueSuffix?: RefOrValue<string>
70
+ width?: RefOrValue<number | string>
71
+ height?: RefOrValue<number | string>
72
+ minValue?: RefOrValue<number | string>
73
+ maxValue?: RefOrValue<number | string>
74
+ animated?: RefOrValue<boolean | string>
75
+ showValues?: RefOrValue<boolean | string>
76
+ showLabels?: RefOrValue<boolean | string>
77
+ showAxis?: RefOrValue<boolean | string>
78
+ tone?: RefOrValue<SemanticTone>
79
+ variant?: RefOrValue<ComponentVariant>
80
+ variantMode?: RefOrValue<ComponentVariantMode>
81
+ classes?: ComputedRef<string>
82
+ resolvedWidth?: ComputedRef<string | number>
83
+ resolvedHeight?: ComputedRef<string | number | undefined>
84
+ resolvedAriaLabel?: ComputedRef<string>
85
+ resolvedEmptyLabel?: ComputedRef<string>
86
+ chartItems?: ComputedRef<ResolvedBarChartItem[]>
87
+ gridLines?: ComputedRef<ResolvedBarChartGridLine[]>
88
+ hasItems?: ComputedRef<boolean>
89
+ zeroLineY?: ComputedRef<number>
90
+ }
91
+
92
+ const VALUE_LABEL_OFFSET = 2.4
93
+ const CATEGORY_LABEL_Y = 60
94
+ const BAR_WIDTH_SHARE = 0.64
95
+ const MIN_VISIBLE_BAR_HEIGHT = 0.35
96
+
97
+ const barChartTemplate = svg`<svg
98
+ class="bar-chart"
99
+ :class="classes"
100
+ viewBox="-6 0 106 64"
101
+ :width="resolvedWidth"
102
+ :height="resolvedHeight"
103
+ role="img"
104
+ :aria-label="resolvedAriaLabel"
105
+ >
106
+ <title r-if="title">{{ title }}</title>
107
+ <desc r-if="description">{{ description }}</desc>
108
+ <g class="bar-chart__grid" r-if="hasItems && showAxis">
109
+ <g r-for="line in gridLines" class="bar-chart__grid-line">
110
+ <line x1="8" x2="94" :y1="line.y" :y2="line.y"/>
111
+ <text x="5.4" :y="line.y">{{ line.formattedValue }}</text>
112
+ </g>
113
+ </g>
114
+ <line
115
+ r-if="hasItems && showAxis"
116
+ class="bar-chart__zero-line"
117
+ x1="8"
118
+ x2="94"
119
+ :y1="zeroLineY"
120
+ :y2="zeroLineY"/>
121
+ <g class="bar-chart__plot" r-if="hasItems">
122
+ <rect
123
+ r-for="item in chartItems"
124
+ class="bar-chart__bar"
125
+ :x="item.x"
126
+ :y="item.y"
127
+ :width="item.width"
128
+ :height="item.height"
129
+ :rx="item.roundedTop"
130
+ :ry="item.roundedTop"
131
+ :fill="item.color"
132
+ :aria-label="item.label + ': ' + item.formattedValue"
133
+ >
134
+ <title>{{ item.label + ': ' + item.formattedValue }}</title>
135
+ <animate
136
+ r-if="animated"
137
+ attributeName="height"
138
+ from="0"
139
+ :to="item.height"
140
+ dur="1720ms"
141
+ calcMode="spline"
142
+ keyTimes="0;1"
143
+ keySplines="0.16 1 0.3 1"
144
+ fill="freeze"/>
145
+ <animate
146
+ r-if="animated"
147
+ attributeName="y"
148
+ :from="zeroLineY"
149
+ :to="item.y"
150
+ dur="1720ms"
151
+ calcMode="spline"
152
+ keyTimes="0;1"
153
+ keySplines="0.16 1 0.3 1"
154
+ fill="freeze"/>
155
+ </rect>
156
+ </g>
157
+ <g class="bar-chart__values" r-if="hasItems && showValues">
158
+ <text
159
+ r-for="item in chartItems"
160
+ class="bar-chart__value"
161
+ :x="item.valueLabelX"
162
+ :y="item.valueLabelY"
163
+ text-anchor="middle"
164
+ :dominant-baseline="item.valueLabelBaseline"
165
+ >
166
+ {{ item.formattedValue }}
167
+ </text>
168
+ </g>
169
+ <g class="bar-chart__labels" r-if="hasItems && showLabels">
170
+ <text
171
+ r-for="item in chartItems"
172
+ class="bar-chart__label"
173
+ :x="item.labelX"
174
+ :y="item.labelY"
175
+ text-anchor="middle"
176
+ >
177
+ {{ item.label }}
178
+ </text>
179
+ </g>
180
+ <text
181
+ r-if="!hasItems"
182
+ class="bar-chart__empty"
183
+ x="50"
184
+ y="30"
185
+ text-anchor="middle"
186
+ >
187
+ {{ resolvedEmptyLabel }}
188
+ </text>
189
+ </svg>`
190
+
191
+ function defineBarChartComponent() {
192
+ return defineComponent<BarChart>(barChartTemplate, {
193
+ props: [
194
+ 'items',
195
+ 'title',
196
+ 'description',
197
+ 'ariaLabel',
198
+ 'emptyLabel',
199
+ 'valueSuffix',
200
+ 'width',
201
+ 'height',
202
+ 'minValue',
203
+ 'maxValue',
204
+ 'animated',
205
+ 'showValues',
206
+ 'showLabels',
207
+ 'showAxis',
208
+ 'tone',
209
+ 'variant',
210
+ 'variantMode',
211
+ ],
212
+ context: (head) => resolveBarChart(head.props),
213
+ })
214
+ }
215
+
216
+ export function defineBarChartComponents() {
217
+ return {
218
+ barChart: defineBarChartComponent(),
219
+ }
220
+ }
221
+
222
+ function resolveBarChart(props: BarChart): BarChart {
223
+ const domain = computed(() => resolveDomain(props))
224
+
225
+ return {
226
+ animated: true,
227
+ showValues: true,
228
+ showLabels: true,
229
+ showAxis: true,
230
+ ...props,
231
+ classes: computed(() =>
232
+ resolveComponentClasses(props, {
233
+ defaultVariant: 'none',
234
+ defaultVariantMode: 'stateless',
235
+ }),
236
+ ),
237
+ resolvedWidth: computed(() =>
238
+ resolveChartSize(props.width, DEFAULT_CARTESIAN_CHART_WIDTH),
239
+ ),
240
+ resolvedHeight: computed(() =>
241
+ resolveChartSize(props.height, DEFAULT_CARTESIAN_CHART_HEIGHT),
242
+ ),
243
+ resolvedAriaLabel: computed(() => resolveAriaLabel(props)),
244
+ resolvedEmptyLabel: computed(
245
+ () => resolveChartText(props.emptyLabel) || DEFAULT_CHART_EMPTY_LABEL,
246
+ ),
247
+ chartItems: computed(() => resolveChartItems(props, domain())),
248
+ gridLines: computed(() =>
249
+ resolveCartesianChartGridLines(domain(), props.valueSuffix),
250
+ ),
251
+ hasItems: computed(() => resolveItems(props.items).length > 0),
252
+ zeroLineY: computed(() => valueToCartesianChartY(0, domain())),
253
+ }
254
+ }
255
+
256
+ function resolveChartItems(
257
+ props: BarChart,
258
+ domain: CartesianChartDomain,
259
+ ): ResolvedBarChartItem[] {
260
+ const items = resolveItems(props.items)
261
+ if (!items.length) return []
262
+
263
+ const laneWidth = CARTESIAN_CHART_PLOT_WIDTH / items.length
264
+ const barWidth = Math.max(1, laneWidth * BAR_WIDTH_SHARE)
265
+ const valueSuffix = resolveChartText(props.valueSuffix)
266
+ const zeroY = valueToCartesianChartY(0, domain)
267
+
268
+ return items.map((item, index) => {
269
+ const valueY = valueToCartesianChartY(item.value, domain)
270
+ const rawHeight = Math.abs(zeroY - valueY)
271
+ const height =
272
+ item.value === 0 ? 0 : Math.max(rawHeight, MIN_VISIBLE_BAR_HEIGHT)
273
+ const isNegative = item.value < 0
274
+ const x =
275
+ CARTESIAN_CHART_PLOT_X + laneWidth * index + (laneWidth - barWidth) / 2
276
+ const y = isNegative
277
+ ? zeroY
278
+ : Math.min(valueY, zeroY) - (height - rawHeight)
279
+ const valueLabelY = isNegative
280
+ ? y + height + VALUE_LABEL_OFFSET
281
+ : Math.max(2, y - VALUE_LABEL_OFFSET)
282
+
283
+ return {
284
+ label: item.label,
285
+ value: item.value,
286
+ formattedValue: formatChartValue(item.value, valueSuffix),
287
+ color:
288
+ item.color || DEFAULT_CHART_COLORS[index % DEFAULT_CHART_COLORS.length],
289
+ x,
290
+ y,
291
+ width: barWidth,
292
+ height,
293
+ valueLabelX: x + barWidth / 2,
294
+ valueLabelY,
295
+ valueLabelBaseline: isNegative ? 'hanging' : 'auto',
296
+ labelX: x + barWidth / 2,
297
+ labelY: CATEGORY_LABEL_Y,
298
+ roundedTop: Math.min(1.4, barWidth / 2, height / 2),
299
+ }
300
+ })
301
+ }
302
+
303
+ interface ResolvedBarChartSourceItem {
304
+ label: string
305
+ value: number
306
+ color: string
307
+ }
308
+
309
+ function resolveItems(items: BarChart['items']): ResolvedBarChartSourceItem[] {
310
+ return (unref(items) || [])
311
+ .map((itemInput, index) => {
312
+ const item = unref(itemInput)
313
+ const value = readChartNumber(item?.value, Number.NaN)
314
+ return {
315
+ label: resolveChartText(item?.label) || `Item ${index + 1}`,
316
+ value,
317
+ color: resolveChartText(item?.color),
318
+ }
319
+ })
320
+ .filter((item) => Number.isFinite(item.value))
321
+ }
322
+
323
+ function resolveDomain(props: BarChart): CartesianChartDomain {
324
+ const values = resolveItems(props.items).map((item) => item.value)
325
+ return resolveCartesianChartDomain({
326
+ values,
327
+ minValue: props.minValue,
328
+ maxValue: props.maxValue,
329
+ })
330
+ }
331
+
332
+ function resolveAriaLabel(props: BarChart) {
333
+ return (
334
+ resolveChartText(props.ariaLabel) ||
335
+ resolveChartText(props.title) ||
336
+ 'Bar chart'
337
+ )
338
+ }
@@ -0,0 +1,81 @@
1
+ import {
2
+ styleBuilder,
3
+ type ThemeMode,
4
+ type ThemePalette,
5
+ themes,
6
+ } from '@purestack/ts-style'
7
+
8
+ const BAR_CHART_VALUE_FONT_SIZE = '0.15625rem'
9
+ const BAR_CHART_LABEL_FONT_SIZE = '0.1375rem'
10
+ const BAR_CHART_AXIS_FONT_SIZE = '0.11875rem'
11
+ const BAR_CHART_EMPTY_FONT_SIZE = '0.1625rem'
12
+
13
+ export function registerBarChartStyles() {
14
+ themes.forEach((theme, palette) => {
15
+ registerBarChartBaseStyles(theme)
16
+ registerBarChartPaintStyles(theme, palette)
17
+ })
18
+ }
19
+
20
+ function registerBarChartBaseStyles(theme: ThemeMode) {
21
+ styleBuilder
22
+ .select('.bar-chart__bar', theme)
23
+ .transition('opacity 180ms ease, filter 180ms ease, transform 180ms ease')
24
+ .transformOrigin('center bottom')
25
+
26
+ styleBuilder
27
+ .select('.bar-chart__plot:hover .bar-chart__bar', theme)
28
+ .opacity('0.72')
29
+
30
+ styleBuilder
31
+ .select('.bar-chart__plot:hover .bar-chart__bar:hover', theme)
32
+ .opacity('1')
33
+ .filter('brightness(1.08)')
34
+
35
+ styleBuilder
36
+ .select(
37
+ '.bar-chart__label, .bar-chart__value, .bar-chart__empty, .bar-chart__grid-line text',
38
+ theme,
39
+ )
40
+ .pointerEvents('none')
41
+ .letterSpacing('normal')
42
+ }
43
+
44
+ function registerBarChartPaintStyles(theme: ThemeMode, palette: ThemePalette) {
45
+ styleBuilder
46
+ .select('.bar-chart__grid-line line', theme)
47
+ .stroke(palette.current.surfaceAlt.rest.border)
48
+ .strokeWidth('0.22')
49
+
50
+ styleBuilder
51
+ .select('.bar-chart__zero-line', theme)
52
+ .stroke(palette.current.border.subtle)
53
+ .strokeWidth('0.36')
54
+
55
+ styleBuilder
56
+ .select('.bar-chart__value', theme)
57
+ .fill(palette.current.text.default)
58
+ .fontSize(BAR_CHART_VALUE_FONT_SIZE)
59
+ .fontWeight(palette.font.weight.w700)
60
+
61
+ styleBuilder
62
+ .select('.bar-chart__label', theme)
63
+ .fill(palette.current.text.subtle)
64
+ .fontSize(BAR_CHART_LABEL_FONT_SIZE)
65
+ .fontWeight(palette.font.weight.w700)
66
+
67
+ styleBuilder
68
+ .select('.bar-chart__grid-line text', theme)
69
+ .fill(palette.current.text.subtle)
70
+ .fontSize(BAR_CHART_AXIS_FONT_SIZE)
71
+ .fontWeight(palette.font.weight.w500)
72
+ .set('dominant-baseline', 'middle')
73
+ .set('text-anchor', 'end')
74
+
75
+ styleBuilder
76
+ .select('.bar-chart__empty', theme)
77
+ .fill(palette.current.text.subtle)
78
+ .fontSize(BAR_CHART_EMPTY_FONT_SIZE)
79
+ .fontWeight(palette.font.weight.w700)
80
+ .set('dominant-baseline', 'middle')
81
+ }
@@ -0,0 +1,191 @@
1
+ import { type TsSsgContext, tryResolveTsSsgContext } from '@purestack/ts-common'
2
+ import type { SemanticTone } from '@purestack/ts-style'
3
+ import { urlNormalizer } from '@purestack/ts-util'
4
+ import {
5
+ type ComputedRef,
6
+ computed,
7
+ defineComponent,
8
+ type Emits,
9
+ html,
10
+ type RefOrValue,
11
+ unref,
12
+ } from 'regor'
13
+ import {
14
+ type ComponentVariant,
15
+ resolveComponentClasses,
16
+ } from '../componentVariant'
17
+
18
+ export type BtnSize = 'sm' | 'md' | 'lg'
19
+ export type BtnType = 'button' | 'submit' | 'reset'
20
+ export type BtnIconPosition = 'start' | 'end'
21
+
22
+ export interface BtnBase {
23
+ tone?: RefOrValue<SemanticTone>
24
+ size?: RefOrValue<BtnSize>
25
+ variant?: RefOrValue<ComponentVariant>
26
+ icon?: RefOrValue<string>
27
+ iconPosition?: RefOrValue<BtnIconPosition>
28
+ iconOnly?: RefOrValue<boolean>
29
+ ariaLabel?: RefOrValue<string>
30
+ classes?: ComputedRef<string>
31
+ showStartIcon?: ComputedRef<boolean>
32
+ showEndIcon?: ComputedRef<boolean>
33
+ emits?: Emits<'click'>
34
+ }
35
+
36
+ export interface Btn extends BtnBase {
37
+ type?: RefOrValue<BtnType>
38
+ disabled?: RefOrValue<boolean>
39
+ buttonType?: RefOrValue<BtnType>
40
+ }
41
+
42
+ export interface BtnLink extends BtnBase {
43
+ href?: RefOrValue<string>
44
+ target?: RefOrValue<string>
45
+ rel?: RefOrValue<string>
46
+ resolvedHref?: ComputedRef<string>
47
+ resolvedRel?: ComputedRef<string>
48
+ }
49
+
50
+ const DEFAULT_BUTTON_VARIANT: ComponentVariant = 'solid'
51
+
52
+ const buttonTemplate = html`<button
53
+ class="btn"
54
+ :class="classes"
55
+ :type="buttonType"
56
+ :disabled="disabled"
57
+ :aria-label="ariaLabel"
58
+ >
59
+ <Icon class="btn__icon" :name="icon" r-if="showStartIcon"/>
60
+ <span class="btn__label" r-if="!iconOnly"><slot></slot></span>
61
+ <Icon class="btn__icon" :name="icon" r-if="showEndIcon"/>
62
+ </button>`
63
+
64
+ const buttonLinkTemplate = html`<a
65
+ class="btn"
66
+ :class="classes"
67
+ :href="resolvedHref"
68
+ :target="target"
69
+ :rel="resolvedRel"
70
+ :aria-label="ariaLabel"
71
+ >
72
+ <Icon class="btn__icon" :name="icon" r-if="showStartIcon"/>
73
+ <span class="btn__label" r-if="!iconOnly"><slot></slot></span>
74
+ <Icon class="btn__icon" :name="icon" r-if="showEndIcon"/>
75
+ </a>`
76
+
77
+ function defineButtonComponent() {
78
+ return defineComponent<Btn>(buttonTemplate, {
79
+ props: [
80
+ 'tone',
81
+ 'size',
82
+ 'variant',
83
+ 'type',
84
+ 'icon',
85
+ 'iconPosition',
86
+ 'iconOnly',
87
+ 'ariaLabel',
88
+ 'disabled',
89
+ ],
90
+ context: (head) => resolveBtn(head.props),
91
+ })
92
+ }
93
+
94
+ function defineButtonLinkComponent() {
95
+ return defineComponent<BtnLink>(buttonLinkTemplate, {
96
+ props: [
97
+ 'href',
98
+ 'target',
99
+ 'rel',
100
+ 'tone',
101
+ 'size',
102
+ 'variant',
103
+ 'icon',
104
+ 'iconPosition',
105
+ 'iconOnly',
106
+ 'ariaLabel',
107
+ ],
108
+ context: (head) => resolveBtnLink(head.props, tryResolveTsSsgContext(head)),
109
+ })
110
+ }
111
+
112
+ export function defineButtonComponents() {
113
+ return {
114
+ btn: defineButtonComponent(),
115
+ btnLink: defineButtonLinkComponent(),
116
+ }
117
+ }
118
+
119
+ function resolveBtn(props: Btn): Btn {
120
+ return {
121
+ ...props,
122
+ classes: computed(() =>
123
+ resolveComponentClasses(props, {
124
+ defaultVariant: DEFAULT_BUTTON_VARIANT,
125
+ classes: resolveButtonStateClasses(props),
126
+ }),
127
+ ),
128
+ buttonType: computed(() => unref(props.type) || 'button'),
129
+ showStartIcon: computed(() => resolveShowStartIcon(props)),
130
+ showEndIcon: computed(() => resolveShowEndIcon(props)),
131
+ }
132
+ }
133
+
134
+ function resolveBtnLink(
135
+ props: BtnLink,
136
+ context: TsSsgContext | undefined,
137
+ ): BtnLink {
138
+ return {
139
+ ...props,
140
+ classes: computed(() =>
141
+ resolveComponentClasses(props, {
142
+ defaultVariant: DEFAULT_BUTTON_VARIANT,
143
+ classes: resolveButtonStateClasses(props),
144
+ }),
145
+ ),
146
+ resolvedHref: computed(() => resolveButtonHref(props, context)),
147
+ resolvedRel: computed(() => resolveButtonRel(props)),
148
+ showStartIcon: computed(() => resolveShowStartIcon(props)),
149
+ showEndIcon: computed(() => resolveShowEndIcon(props)),
150
+ }
151
+ }
152
+
153
+ function resolveButtonStateClasses(props: BtnBase) {
154
+ const size = unref(props.size)
155
+ const hasIcon = !!unref(props.icon)
156
+ const iconPosition = unref(props.iconPosition)
157
+ const iconOnly = unref(props.iconOnly)
158
+ const classes = [size ? `btn--${size}` : '']
159
+ if (!hasIcon) return classes
160
+ classes.push(
161
+ iconOnly
162
+ ? 'btn--icon-only'
163
+ : iconPosition === 'end'
164
+ ? 'btn--icon-end'
165
+ : 'btn--icon-start',
166
+ )
167
+ return classes
168
+ }
169
+
170
+ function resolveShowStartIcon(props: BtnBase) {
171
+ const hasIcon = !!unref(props.icon)
172
+ return hasIcon && unref(props.iconPosition) !== 'end'
173
+ }
174
+
175
+ function resolveShowEndIcon(props: BtnBase) {
176
+ const hasIcon = !!unref(props.icon)
177
+ return hasIcon && unref(props.iconPosition) === 'end'
178
+ }
179
+
180
+ function resolveButtonHref(props: BtnLink, context: TsSsgContext | undefined) {
181
+ const normalized = urlNormalizer.normalizeHref(unref(props.href))
182
+ return normalized
183
+ ? (context?.resolvePublicHref(normalized) ?? normalized)
184
+ : undefined
185
+ }
186
+
187
+ function resolveButtonRel(props: BtnLink) {
188
+ const rel = unref(props.rel)
189
+ if (rel) return rel
190
+ return unref(props.target) === '_blank' ? 'noopener noreferrer' : undefined
191
+ }