@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,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
|
+
}
|