|
| 1 | +import type { |
| 2 | + ComponentVariant, |
| 3 | + ComponentVariantMode, |
| 4 | +} from '@purestack/ts-components' |
| 5 | +import { |
| 6 | + getThemePaletteVar, |
| 7 | + SEMANTIC_TONES, |
| 8 | + type SemanticTone, |
| 9 | +} from '@purestack/ts-style' |
| 10 | +import { |
| 11 | + defineComponent, |
| 12 | + html, |
| 13 | + type Ref, |
| 14 | + type RefOrValue, |
| 15 | + ref, |
| 16 | + unref, |
| 17 | +} from 'regor' |
| 18 | + |
| 19 | +const variants: ComponentVariant[] = [ |
| 20 | + 'solid', |
| 21 | + 'surface', |
| 22 | + 'surfaceAlt', |
| 23 | + 'spotlight', |
| 24 | + 'glass', |
| 25 | + 'flat', |
| 26 | + 'flatAlt', |
| 27 | + 'flatSolid', |
| 28 | + 'outlineFill', |
| 29 | + 'outline', |
| 30 | + 'subtle', |
| 31 | + 'subtleBtn', |
| 32 | + 'link', |
| 33 | + 'sheen', |
| 34 | + 'underline', |
| 35 | + 'rail', |
| 36 | + 'bracket', |
| 37 | + 'none', |
| 38 | +] |
| 39 | + |
| 40 | +export interface ChartAppearanceState { |
| 41 | + chartTone: Ref<SemanticTone> |
| 42 | + chartVariant: Ref<ComponentVariant> |
| 43 | + chartVariantMode: Ref<ComponentVariantMode> |
| 44 | + chartWidth: Ref<number | string> |
| 45 | + resetAppearance: () => void |
| 46 | +} |
| 47 | + |
| 48 | +export function createChartAppearanceState(): ChartAppearanceState { |
| 49 | + const tone = ref<SemanticTone>('neutral') |
| 50 | + const variant = ref<ComponentVariant>('none') |
| 51 | + const variantMode = ref<ComponentVariantMode>('stateless') |
| 52 | + const width = ref<number | string>('') |
| 53 | + return { |
| 54 | + chartTone: tone, |
| 55 | + chartVariant: variant, |
| 56 | + chartVariantMode: variantMode, |
| 57 | + chartWidth: width, |
| 58 | + resetAppearance: () => { |
| 59 | + tone('neutral') |
| 60 | + variant('none') |
| 61 | + variantMode('stateless') |
| 62 | + width('') |
| 63 | + }, |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +export function defineChartAppearanceControls() { |
| 68 | + return defineComponent<{ |
| 69 | + tone: Ref<SemanticTone> |
| 70 | + variant: Ref<ComponentVariant> |
| 71 | + variantMode: Ref<ComponentVariantMode> |
| 72 | + width: Ref<number | string> |
| 73 | + showWidth?: RefOrValue<boolean> |
| 74 | + prefix: RefOrValue<string> |
| 75 | + }>( |
| 76 | + html`<Grid columns="1" columnsMd="3"> |
| 77 | + <FormSelectField :id="prefix + '-tone'" label="Chart tone" :model="tone" :options="tones"/> |
| 78 | + <FormSelectField :id="prefix + '-variant'" label="Chart variant" :model="variant" :options="variants"/> |
| 79 | + <FormSelectField :id="prefix + '-mode'" label="Variant mode" :model="variantMode" :options="modes"/> |
| 80 | + <FormInputField r-if="showWidth" :id="prefix + '-width'" label="SVG width (px; blank is responsive)" type="number" min="240" :model="width"/> |
| 81 | + </Grid>`, |
| 82 | + { |
| 83 | + props: ['tone', 'variant', 'variantMode', 'width', 'showWidth', 'prefix'], |
| 84 | + context: (head) => ({ |
| 85 | + ...head.props, |
| 86 | + tones: SEMANTIC_TONES.map((value) => ({ label: value, value })), |
| 87 | + variants: variants.map((value) => ({ label: value, value })), |
| 88 | + modes: [ |
| 89 | + { label: 'Stateless · resting appearance', value: 'stateless' }, |
| 90 | + { label: 'Stateful · hover and active', value: 'stateful' }, |
| 91 | + ], |
| 92 | + }), |
| 93 | + }, |
| 94 | + ) |
| 95 | +} |
| 96 | + |
| 97 | +export function defineChartAppearanceGallery() { |
| 98 | + return defineComponent<{ |
| 99 | + component: RefOrValue<string> |
| 100 | + axis: RefOrValue<'tone' | 'variant' | 'mode'> |
| 101 | + }>( |
| 102 | + html`<div class="component-appearance-grid data-chart-gallery"> |
| 103 | + <div class="component-appearance-cell" r-for="sample in samples"> |
| 104 | + <code>{{ sample.label }}</code> |
| 105 | + <BarChart r-if="component === 'BarChart'" :items="sample.items" :tone="sample.tone" :variant="sample.variant" :variantMode="sample.mode" width="100%" height="160" :animated="false" :ariaLabel="sample.label + ': Monday 24, Tuesday 38, Wednesday -12'"/> |
| 106 | + <LineChart r-if="component === 'LineChart'" :series="sample.series" :tone="sample.tone" :variant="sample.variant" :variantMode="sample.mode" width="100%" height="160" :showArea="true" :showPoints="true" :animated="false" :ariaLabel="sample.label + ': requests 18, 32, 25, 48'"/> |
| 107 | + <DoughnutChart r-if="component === 'DoughnutChart'" :segments="sample.segments" :tone="sample.tone" :variant="sample.variant" :variantMode="sample.mode" size="160" centerLabel="Tasks" centerValue="30" :animated="false" :ariaLabel="sample.label + ': complete 18, review 7, planned 5'"/> |
| 108 | + </div> |
| 109 | + </div>`, |
| 110 | + { |
| 111 | + props: ['component', 'axis'], |
| 112 | + context: (head) => { |
| 113 | + const axis = unref(head.props.axis) |
| 114 | + const values = |
| 115 | + axis === 'tone' |
| 116 | + ? SEMANTIC_TONES |
| 117 | + : axis === 'variant' |
| 118 | + ? variants |
| 119 | + : ['stateless', 'stateful'] |
| 120 | + return { |
| 121 | + ...head.props, |
| 122 | + samples: values.map((label) => { |
| 123 | + const tone = axis === 'tone' ? label : 'accent' |
| 124 | + const variant = |
| 125 | + axis === 'variant' |
| 126 | + ? label |
| 127 | + : axis === 'mode' |
| 128 | + ? 'outlineFill' |
| 129 | + : 'surface' |
| 130 | + const color = getThemePaletteVar( |
| 131 | + ['solid', 'flatSolid'].includes(variant) |
| 132 | + ? `semanticTone.${tone}.button.rest.text` |
| 133 | + : ['neutral', 'ghost'].includes(tone) |
| 134 | + ? `semanticTone.${tone}.text.default` |
| 135 | + : `semanticTone.${tone}.button.rest.bgcolor`, |
| 136 | + ) |
| 137 | + return { |
| 138 | + label, |
| 139 | + tone, |
| 140 | + variant, |
| 141 | + mode: axis === 'mode' ? label : 'stateless', |
| 142 | + items: [ |
| 143 | + { label: 'Mon', value: 24, color }, |
| 144 | + { label: 'Tue', value: 38, color }, |
| 145 | + { label: 'Wed', value: -12, color }, |
| 146 | + ], |
| 147 | + series: [ |
| 148 | + { |
| 149 | + label: 'Requests', |
| 150 | + color, |
| 151 | + points: [18, 32, 25, 48].map((value, index) => ({ |
| 152 | + label: ['Mon', 'Tue', 'Wed', 'Thu'][index], |
| 153 | + value, |
| 154 | + })), |
| 155 | + }, |
| 156 | + ], |
| 157 | + segments: [ |
| 158 | + { label: 'Complete', value: 18, color }, |
| 159 | + { label: 'Review', value: 7 }, |
| 160 | + { label: 'Planned', value: 5 }, |
| 161 | + ], |
| 162 | + } |
| 163 | + }), |
| 164 | + } |
| 165 | + }, |
| 166 | + }, |
| 167 | + ) |
| 168 | +} |
0 commit comments