frappe-ui 1.0.0-beta.62 → 1.0.0-beta.64
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/experimental/CodeEditor/CodeEditor.api.md +1 -1
- package/experimental/CodeEditor/CodeEditor.md +1 -1
- package/experimental/CodeEditor/CodeEditor.playground.vue +1 -1
- package/experimental/CodeEditor/CodeEditor.vue +2 -2
- package/experimental/CodeEditor/stories/Sizes.vue +1 -1
- package/experimental/CodeEditor/types.ts +1 -1
- package/experimental/MultiEmailInput/MultiEmailInput.cy.ts +27 -0
- package/experimental/MultiEmailInput/MultiEmailInput.vue +9 -1
- package/package.json +1 -1
- package/src/charts/areaChartOptions.test.ts +2 -3
- package/src/charts/barChartOptions.test.ts +8 -8
- package/src/charts/chartColors.test.ts +35 -15
- package/src/charts/comboChartOptions.test.ts +1 -1
- package/src/charts/donutChartOptions.test.ts +1 -1
- package/src/charts/heatmapOptions.test.ts +7 -13
- package/src/charts/lineChartOptions.test.ts +1 -1
- package/src/charts/style.css +8 -5
- package/src/charts/tokens.ts +51 -31
- package/src/components/Checkbox/Checkbox.cy.ts +3 -3
- package/src/components/Combobox/Combobox.api.md +2 -2
- package/src/components/Combobox/Combobox.playground.vue +1 -1
- package/src/components/Combobox/Combobox.vue +5 -0
- package/src/components/Combobox/stories/Clearable.vue +5 -5
- package/src/components/Combobox/types.ts +9 -8
- package/src/components/ContextMenu/index.ts +1 -0
- package/src/components/ContextMenu/types.ts +16 -0
- package/src/components/DesktopShell/DesktopShell.api.md +1 -1
- package/src/components/DesktopShell/DesktopShell.md +6 -5
- package/src/components/DesktopShell/DesktopShell.vue +1 -1
- package/src/components/DesktopShell/stories/Default.vue +8 -8
- package/src/components/Divider/Divider.api.md +4 -4
- package/src/components/Divider/index.ts +1 -1
- package/src/components/Divider/types.ts +24 -1
- package/src/components/Duration/Duration.playground.vue +1 -1
- package/src/components/Duration/stories/Sizes.vue +1 -1
- package/src/components/FormControl/FormControl.api.md +2 -2
- package/src/components/FormControl/FormControl.cy.ts +40 -0
- package/src/components/FormControl/FormControl.playground.vue +2 -0
- package/src/components/FormControl/FormControl.vue +6 -1
- package/src/components/FormControl/types.ts +7 -2
- package/src/components/FormLabel/FormLabel.api.md +0 -7
- package/src/components/FormLabel/FormLabel.cy.ts +24 -5
- package/src/components/FormLabel/FormLabel.playground.vue +1 -11
- package/src/components/FormLabel/FormLabel.vue +7 -12
- package/src/components/FormLabel/types.ts +0 -3
- package/src/components/HoverCard/HoverCard.api.md +1 -1
- package/src/components/HoverCard/types.ts +3 -0
- package/src/components/InputLabeling/InputDescription.vue +1 -1
- package/src/components/InputLabeling/InputLabel.vue +9 -4
- package/src/components/ItemListRow/ItemListRow.api.md +1 -1
- package/src/components/ItemListRow/ItemListRow.vue +13 -7
- package/src/components/ItemListRow/types.ts +7 -1
- package/src/components/KeyboardShortcutsDialog/KeyboardShortcutsDialog.api.md +1 -1
- package/src/components/KeyboardShortcutsDialog/types.ts +3 -0
- package/src/components/MultiSelect/MultiSelect.api.md +3 -3
- package/src/components/MultiSelect/MultiSelect.playground.vue +1 -1
- package/src/components/MultiSelect/MultiSelect.vue +5 -2
- package/src/components/MultiSelect/types.ts +4 -8
- package/src/components/Password/Password.playground.vue +1 -1
- package/src/components/Password/stories/Sizes.vue +1 -1
- package/src/components/Rating/Rating.cy.ts +1 -1
- package/src/components/Rating/Rating.playground.vue +1 -1
- package/src/components/Rating/Rating.vue +14 -8
- package/src/components/Rating/stories/Sizes.vue +1 -1
- package/src/components/Select/Select.api.md +2 -2
- package/src/components/Select/Select.cy.ts +19 -1
- package/src/components/Select/Select.playground.vue +1 -1
- package/src/components/Select/types.ts +3 -2
- package/src/components/Select/utils.ts +12 -6
- package/src/components/{Rail/Rail.api.md → SidebarRail/SidebarRail.api.md} +11 -11
- package/src/components/{Rail/Rail.cy.ts → SidebarRail/SidebarRail.cy.ts} +31 -31
- package/src/components/SidebarRail/SidebarRail.md +36 -0
- package/src/components/{Rail/Rail.vue → SidebarRail/SidebarRail.vue} +2 -2
- package/src/components/{Rail/RailItem.vue → SidebarRail/SidebarRailItem.vue} +12 -12
- package/src/components/{Rail/RailItemBadge.vue → SidebarRail/SidebarRailItemBadge.vue} +1 -1
- package/src/components/SidebarRail/index.ts +4 -0
- package/src/components/{Rail → SidebarRail}/stories/Default.vue +10 -10
- package/src/components/{Rail → SidebarRail}/types.ts +1 -1
- package/src/components/Switch/Switch.cy.ts +2 -2
- package/src/components/TabButtons/TabButtons.api.md +4 -4
- package/src/components/TabButtons/TabButtons.vue +3 -0
- package/src/components/TabButtons/index.ts +1 -0
- package/src/components/TabButtons/types.ts +7 -0
- package/src/components/TextInput/TextInput.cy.ts +108 -4
- package/src/components/TextInput/TextInput.playground.vue +1 -1
- package/src/components/TextInput/TextInput.vue +60 -26
- package/src/components/TextInput/stories/Sizes.vue +1 -1
- package/src/components/Textarea/Textarea.api.md +1 -1
- package/src/components/Textarea/Textarea.cy.ts +65 -34
- package/src/components/Textarea/Textarea.playground.vue +1 -1
- package/src/components/Textarea/Textarea.vue +39 -14
- package/src/components/Textarea/stories/Sizes.vue +1 -1
- package/src/components/Textarea/types.ts +5 -1
- package/src/components/TimePicker/stories/SizesAndVariants.vue +1 -1
- package/src/components/Tree/Tree.vue +2 -6
- package/src/components/Tree/types.ts +8 -0
- package/src/components/shared/picker/PickerShell.vue +34 -37
- package/src/components/shared/selection/utils.test.ts +54 -2
- package/src/components/shared/selection/utils.ts +64 -21
- package/src/components/shared/slotReactivity.cy.ts +36 -0
- package/src/components/shared/tabs/Pill.vue +2 -3
- package/src/components/shared/tabs/styles.ts +1 -1
- package/src/composables/inputTypes.ts +14 -2
- package/src/composables/usePortalTarget.spec.ts +6 -5
- package/src/index.ts +12 -1
- package/src/molecules/editor/components/MediaNodeView.test.ts +30 -0
- package/src/molecules/editor/components/MediaNodeView.vue +13 -8
- package/src/molecules/editor/components/VideoControls.test.ts +118 -2
- package/src/molecules/editor/components/VideoControls.vue +89 -11
- package/src/molecules/list/List.cy.ts +426 -0
- package/src/molecules/list/List.ssr.test.ts +59 -0
- package/src/molecules/list/List.vue +92 -7
- package/src/molecules/list/index.ts +2 -0
- package/src/molecules/list/list-context.ts +2 -1
- package/src/molecules/list/list.api.md +3 -3
- package/src/molecules/list/list.md +152 -33
- package/src/molecules/list/stories/Columns.vue +15 -9
- package/src/molecules/list/stories/Responsive.vue +56 -0
- package/src/molecules/list/stories/RowActions.vue +123 -0
- package/src/molecules/list/style.css +67 -26
- package/src/molecules/list/types.ts +51 -9
- package/tailwind/listColumns.js +191 -0
- package/tailwind/listColumns.test.js +358 -0
- package/tailwind/plugin.js +5 -6
- package/src/components/Rail/Rail.md +0 -30
- package/src/components/Rail/index.ts +0 -2
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
name: 'size',
|
|
44
|
-
description: 'Size token, mirroring frappe-ui inputs. Scales the editor\'s font size and\nminimum height (`sm | md | lg
|
|
44
|
+
description: 'Size token, mirroring frappe-ui inputs. Scales the editor\'s font size and\nminimum height (`xs | sm | md | lg`). Defaults to `md`.',
|
|
45
45
|
required: false,
|
|
46
46
|
type: 'InputSize',
|
|
47
47
|
default: '"md"'
|
|
@@ -40,7 +40,7 @@ and loses the affordance that it's an editable field.
|
|
|
40
40
|
|
|
41
41
|
## Sizes
|
|
42
42
|
|
|
43
|
-
`size` mirrors the frappe-ui input sizes (`sm | md | lg
|
|
43
|
+
`size` mirrors the frappe-ui input sizes (`xs | sm | md | lg`, default `md`),
|
|
44
44
|
scaling the editor's font size and minimum height.
|
|
45
45
|
|
|
46
46
|
<ComponentPreview name="CodeEditor-Sizes" />
|
|
@@ -43,10 +43,10 @@ const knobs: Knob[] = [
|
|
|
43
43
|
type: 'tabs',
|
|
44
44
|
default: 'md',
|
|
45
45
|
options: [
|
|
46
|
+
{ label: 'xs', value: 'xs' },
|
|
46
47
|
{ label: 'sm', value: 'sm' },
|
|
47
48
|
{ label: 'md', value: 'md' },
|
|
48
49
|
{ label: 'lg', value: 'lg' },
|
|
49
|
-
{ label: 'xl', value: 'xl' },
|
|
50
50
|
],
|
|
51
51
|
},
|
|
52
52
|
{ name: 'placeholder', type: 'text', default: '', width: '12rem' },
|
|
@@ -288,17 +288,17 @@ function buildVariant(v?: string, disabled?: boolean): Extension {
|
|
|
288
288
|
return cmView.EditorView.theme(fragments[v ?? 'subtle'] ?? fragments.subtle)
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
// Per-size font scale + content min-height, mirroring frappe-ui's `sm|md|lg
|
|
291
|
+
// Per-size font scale + content min-height, mirroring frappe-ui's `xs|sm|md|lg`
|
|
292
292
|
// input sizes. This compartment is the SOLE source of `&` font size and
|
|
293
293
|
// `.cm-content` min-height — the base `theme` deliberately omits both so there's
|
|
294
294
|
// no equal-specificity fight whose winner depends on StyleModule order.
|
|
295
295
|
function buildSize(s?: string): Extension {
|
|
296
296
|
if (!cmView) return []
|
|
297
297
|
const sizes: Record<string, { fontSize: string; minHeight: string }> = {
|
|
298
|
+
xs: { fontSize: '11px', minHeight: '2.5rem' },
|
|
298
299
|
sm: { fontSize: '12px', minHeight: '3.5rem' },
|
|
299
300
|
md: { fontSize: '13px', minHeight: '4.5rem' },
|
|
300
301
|
lg: { fontSize: '14px', minHeight: '6rem' },
|
|
301
|
-
xl: { fontSize: '16px', minHeight: '7.5rem' },
|
|
302
302
|
}
|
|
303
303
|
const cfg = sizes[s ?? 'md'] ?? sizes.md
|
|
304
304
|
return cmView.EditorView.theme({
|
|
@@ -7,9 +7,9 @@ const code = ref(`const sum = (a, b) => a + b`)
|
|
|
7
7
|
|
|
8
8
|
<template>
|
|
9
9
|
<div class="w-full max-w-xl space-y-4">
|
|
10
|
+
<CodeEditor v-model="code" language="javascript" size="xs" />
|
|
10
11
|
<CodeEditor v-model="code" language="javascript" size="sm" />
|
|
11
12
|
<CodeEditor v-model="code" language="javascript" size="md" />
|
|
12
13
|
<CodeEditor v-model="code" language="javascript" size="lg" />
|
|
13
|
-
<CodeEditor v-model="code" language="javascript" size="xl" />
|
|
14
14
|
</div>
|
|
15
15
|
</template>
|
|
@@ -42,7 +42,7 @@ export interface CodeEditorProps extends InputLabelingProps {
|
|
|
42
42
|
variant?: Exclude<InputVariant, 'ghost'>
|
|
43
43
|
/**
|
|
44
44
|
* Size token, mirroring frappe-ui inputs. Scales the editor's font size and
|
|
45
|
-
* minimum height (`sm | md | lg
|
|
45
|
+
* minimum height (`xs | sm | md | lg`). Defaults to `md`.
|
|
46
46
|
*/
|
|
47
47
|
size?: InputSize
|
|
48
48
|
}
|
|
@@ -17,6 +17,7 @@ function host(
|
|
|
17
17
|
required?: boolean
|
|
18
18
|
error?: string
|
|
19
19
|
disabled?: boolean
|
|
20
|
+
size?: 'xs' | 'sm' | 'md' | 'lg'
|
|
20
21
|
} = {},
|
|
21
22
|
) {
|
|
22
23
|
return defineComponent({
|
|
@@ -46,6 +47,7 @@ function host(
|
|
|
46
47
|
required: options.required,
|
|
47
48
|
error: options.error,
|
|
48
49
|
disabled: options.disabled,
|
|
50
|
+
size: options.size,
|
|
49
51
|
placeholder: 'Add email…',
|
|
50
52
|
}),
|
|
51
53
|
]
|
|
@@ -152,4 +154,29 @@ describe('MultiEmailInput', () => {
|
|
|
152
154
|
cy.mount(host({ disabled: true }))
|
|
153
155
|
cy.get('[data-slot="input"]').should('have.attr', 'disabled')
|
|
154
156
|
})
|
|
157
|
+
|
|
158
|
+
// The option-row avatar has to shrink with the row. `md` (24px) is the whole
|
|
159
|
+
// height of an `xs` row before its padding, so a size-invariant avatar makes
|
|
160
|
+
// the smallest control draw the biggest picture.
|
|
161
|
+
const rowAvatarHeights: Array<[size: 'xs' | 'sm' | 'md' | 'lg', px: number]> =
|
|
162
|
+
[
|
|
163
|
+
['xs', 16],
|
|
164
|
+
['sm', 20],
|
|
165
|
+
['md', 24],
|
|
166
|
+
['lg', 24],
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
for (const [size, px] of rowAvatarHeights) {
|
|
170
|
+
it(`draws a ${px}px option-row avatar at size="${size}"`, () => {
|
|
171
|
+
cy.mount(host({ size }))
|
|
172
|
+
cy.get('[data-slot="input"]').click()
|
|
173
|
+
cy.get('[data-slot="item"]')
|
|
174
|
+
.first()
|
|
175
|
+
.find('[data-slot="item-prefix"] > div')
|
|
176
|
+
.first()
|
|
177
|
+
.then(($avatar) => {
|
|
178
|
+
expect($avatar[0].getBoundingClientRect().height).to.equal(px)
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
}
|
|
155
182
|
})
|
|
@@ -166,7 +166,15 @@ const showEmpty = computed(
|
|
|
166
166
|
() => !props.loading && !suggestions.value.length && !showCreateOption.value,
|
|
167
167
|
)
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
// Avatar has its own scale (`xs` 16px, `sm` 20px, `md` 24px), and the option
|
|
170
|
+
// row is `min-h-6`/`min-h-7`/`min-h-8`/`min-h-10` plus `ItemListRow`'s own
|
|
171
|
+
// padding. Step the avatar down with the row for the two small sizes rather
|
|
172
|
+
// than forwarding a size the row cannot draw — a `md` avatar is 24px, the
|
|
173
|
+
// whole height of an `xs` row before its `py-1`. `md` and `lg` rows both fit
|
|
174
|
+
// `md`; a 28px `lg` avatar would leave no room in a 32px `md` row.
|
|
175
|
+
const rowAvatarSize = computed(() =>
|
|
176
|
+
props.size === 'xs' ? 'xs' : props.size === 'sm' ? 'sm' : 'md',
|
|
177
|
+
)
|
|
170
178
|
|
|
171
179
|
const boxClasses = computed(() => [
|
|
172
180
|
'flex flex-wrap items-center gap-1.5 rounded-4 bg-surface-gray-2 px-1.5 py-1 transition-colors focus-within:ring-2 focus-within:ring-outline-gray-3',
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import type { AxisChartConfig } from './types'
|
|
|
9
9
|
|
|
10
10
|
const tokens: ChartTokens = {
|
|
11
11
|
categorical: ['#111111', '#222222', '#333333'],
|
|
12
|
-
sequential: ['#000011', '#000022', '#000033'
|
|
12
|
+
sequential: ['#000011', '#000022', '#000033'],
|
|
13
13
|
diverging: ['#001100', '#002200', '#003300'],
|
|
14
14
|
axisLabel: 'ink-5',
|
|
15
15
|
axisTitle: 'ink-7',
|
|
@@ -169,9 +169,8 @@ describe('area chart option', () => {
|
|
|
169
169
|
build({
|
|
170
170
|
fillOpacity: 0.5,
|
|
171
171
|
series: [{ name: 'sales', fillOpacity: 0.25 }],
|
|
172
|
-
// A lone series takes the ramp's mid stop, hence the shifted blue.
|
|
173
172
|
}).series[0].areaStyle.color.colorStops[0].color,
|
|
174
|
-
).toBe('rgba(0, 0,
|
|
173
|
+
).toBe('rgba(0, 0, 17, 0.25)')
|
|
175
174
|
})
|
|
176
175
|
|
|
177
176
|
it('falls back to a flat fill for a color it cannot add alpha to', () => {
|
|
@@ -7,8 +7,8 @@ import type { AxisChartConfig, AxisChartSeriesConfig } from './types'
|
|
|
7
7
|
|
|
8
8
|
const tokens: ChartTokens = {
|
|
9
9
|
categorical: ['#111111', '#222222', '#333333'],
|
|
10
|
-
//
|
|
11
|
-
sequential: ['#000011', '#000022', '#000033'
|
|
10
|
+
// Three stops so even spacing across the ramp is visible.
|
|
11
|
+
sequential: ['#000011', '#000022', '#000033'],
|
|
12
12
|
diverging: ['#001100', '#002200', '#003300'],
|
|
13
13
|
axisLabel: 'ink-5',
|
|
14
14
|
axisTitle: 'ink-7',
|
|
@@ -70,13 +70,13 @@ describe('resolveSeriesColors', () => {
|
|
|
70
70
|
).toEqual({ sales: '#000011', refunds: 'red' })
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
it('gives a lone sequential series
|
|
73
|
+
it('gives a lone sequential series the darkest stop', () => {
|
|
74
74
|
expect(colorsFor({ series: [{ name: 'sales' }] })).toEqual({
|
|
75
|
-
sales: '#
|
|
75
|
+
sales: '#000011',
|
|
76
76
|
})
|
|
77
77
|
})
|
|
78
78
|
|
|
79
|
-
it('spaces sequential series out dark to light
|
|
79
|
+
it('spaces sequential series out dark to light', () => {
|
|
80
80
|
expect(Object.values(colorsFor({ series: named(3) }))).toEqual([
|
|
81
81
|
'#000011',
|
|
82
82
|
'#000022',
|
|
@@ -84,10 +84,10 @@ describe('resolveSeriesColors', () => {
|
|
|
84
84
|
])
|
|
85
85
|
})
|
|
86
86
|
|
|
87
|
-
it('cycles the sequential ramp once there are more series than
|
|
88
|
-
const colors = colorsFor({ series: named(
|
|
87
|
+
it('cycles the sequential ramp once there are more series than stops', () => {
|
|
88
|
+
const colors = colorsFor({ series: named(4) })
|
|
89
89
|
expect(colors.a).toBe('#000011')
|
|
90
|
-
expect(colors.
|
|
90
|
+
expect(colors.d).toBe(colors.a)
|
|
91
91
|
})
|
|
92
92
|
|
|
93
93
|
it('cycles the categorical ramp in series order', () => {
|
|
@@ -25,9 +25,6 @@ const tokens: ChartTokens = {
|
|
|
25
25
|
backdrop: '#ffffff',
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/** The sequential ramp minus the two palest stops, as every chart reads it. */
|
|
29
|
-
const USABLE_SEQUENTIAL = tokens.sequential.slice(0, 7)
|
|
30
|
-
|
|
31
28
|
describe('chartColors: precedence', () => {
|
|
32
29
|
it("draws in the caller's own colors when they passed a list", () => {
|
|
33
30
|
expect(
|
|
@@ -56,7 +53,7 @@ describe('chartColors: precedence', () => {
|
|
|
56
53
|
).toEqual(tokens.categorical.slice(0, 2))
|
|
57
54
|
expect(
|
|
58
55
|
chartColors([], tokens, { fallback: 'sequential', count: 'ramp' }),
|
|
59
|
-
).toEqual(
|
|
56
|
+
).toEqual(tokens.sequential)
|
|
60
57
|
})
|
|
61
58
|
})
|
|
62
59
|
|
|
@@ -90,6 +87,38 @@ describe('chartColors: one color per thing drawn', () => {
|
|
|
90
87
|
})
|
|
91
88
|
})
|
|
92
89
|
|
|
90
|
+
describe('chartColors: how a sequential ramp is spent', () => {
|
|
91
|
+
const stops = (count: number) =>
|
|
92
|
+
chartColors('sequential', tokens, { fallback: 'sequential', count }).map(
|
|
93
|
+
(color) => tokens.sequential.indexOf(color) + 1,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
it('gives a lone series the deep end', () => {
|
|
97
|
+
expect(stops(1)).toEqual([1])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('keeps a small chart a prefix of the next size up', () => {
|
|
101
|
+
expect(stops(2)).toEqual([1, 4])
|
|
102
|
+
expect(stops(3)).toEqual([1, 4, 7])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('stays inside the first seven stops up to seven series', () => {
|
|
106
|
+
expect(stops(4)).toEqual([1, 3, 5, 7])
|
|
107
|
+
expect(stops(5)).toEqual([1, 3, 4, 6, 7])
|
|
108
|
+
expect(stops(6)).toEqual([1, 2, 3, 5, 6, 7])
|
|
109
|
+
expect(stops(7)).toEqual([1, 2, 3, 4, 5, 6, 7])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('reaches the pale stops only once the series outnumber the span', () => {
|
|
113
|
+
expect(stops(8)).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
|
|
114
|
+
expect(stops(9)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('cycles once there are more series than stops', () => {
|
|
118
|
+
expect(stops(10)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 1])
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
93
122
|
describe('chartColors: the ramp itself', () => {
|
|
94
123
|
it('gives the stops rather than a slot each', () => {
|
|
95
124
|
expect(
|
|
@@ -97,16 +126,7 @@ describe('chartColors: the ramp itself', () => {
|
|
|
97
126
|
fallback: 'sequential',
|
|
98
127
|
count: 'ramp',
|
|
99
128
|
}),
|
|
100
|
-
).toEqual(
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('trims the sequential stops that vanish against a card', () => {
|
|
104
|
-
const ramp = chartColors(undefined, tokens, {
|
|
105
|
-
fallback: 'sequential',
|
|
106
|
-
count: 'ramp',
|
|
107
|
-
})
|
|
108
|
-
expect(ramp).not.toContain('#f5f5f5')
|
|
109
|
-
expect(ramp).not.toContain('#e0e0e0')
|
|
129
|
+
).toEqual(tokens.sequential)
|
|
110
130
|
})
|
|
111
131
|
|
|
112
132
|
it('takes a diverging ramp end to end', () => {
|
|
@@ -151,7 +171,7 @@ describe('chartColors: which end of the ramp leads', () => {
|
|
|
151
171
|
count: 'ramp',
|
|
152
172
|
deepEnd: 'last',
|
|
153
173
|
}),
|
|
154
|
-
).toEqual([...
|
|
174
|
+
).toEqual([...tokens.sequential].reverse())
|
|
155
175
|
})
|
|
156
176
|
|
|
157
177
|
it('leaves a categorical set alone, having no order to reverse', () => {
|
|
@@ -9,7 +9,7 @@ import type { AxisChartConfig, ChartMark } from './types'
|
|
|
9
9
|
|
|
10
10
|
const tokens: ChartTokens = {
|
|
11
11
|
categorical: ['#111111', '#222222', '#333333'],
|
|
12
|
-
sequential: ['#000011', '#000022', '#000033'
|
|
12
|
+
sequential: ['#000011', '#000022', '#000033'],
|
|
13
13
|
diverging: ['#001100', '#002200', '#003300'],
|
|
14
14
|
axisLabel: 'ink-5',
|
|
15
15
|
axisTitle: 'ink-7',
|
|
@@ -6,7 +6,7 @@ import type { DonutChartConfig } from './types'
|
|
|
6
6
|
|
|
7
7
|
const tokens: ChartTokens = {
|
|
8
8
|
categorical: ['#111111', '#222222', '#333333'],
|
|
9
|
-
sequential: ['#000011', '#000022', '#000033'
|
|
9
|
+
sequential: ['#000011', '#000022', '#000033'],
|
|
10
10
|
diverging: ['#001100', '#002200', '#003300'],
|
|
11
11
|
axisLabel: 'ink-5',
|
|
12
12
|
axisTitle: 'ink-7',
|
|
@@ -203,18 +203,10 @@ describe('buildHeatmapMatrix', () => {
|
|
|
203
203
|
})
|
|
204
204
|
|
|
205
205
|
describe('heatmapRampStops', () => {
|
|
206
|
-
it('reverses the sequential ramp
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
'#cccccc',
|
|
211
|
-
'#aaaaaa',
|
|
212
|
-
'#8a8a8a',
|
|
213
|
-
'#5a5a5a',
|
|
214
|
-
'#3a3a3a',
|
|
215
|
-
'#1a1a1a',
|
|
216
|
-
'#0a0a0a',
|
|
217
|
-
])
|
|
206
|
+
it('reverses the sequential ramp, palest first', () => {
|
|
207
|
+
expect(heatmapRampStops(config(), tokens)).toEqual(
|
|
208
|
+
[...tokens.sequential].reverse(),
|
|
209
|
+
)
|
|
218
210
|
})
|
|
219
211
|
|
|
220
212
|
it('takes the diverging ramp as authored, cool end first', () => {
|
|
@@ -347,7 +339,9 @@ describe('buildHeatmapOption', () => {
|
|
|
347
339
|
const resting = cells[index].color
|
|
348
340
|
const hovered = item.emphasis.itemStyle.color
|
|
349
341
|
expect(hovered).toBe(hoverCellColor(resting))
|
|
350
|
-
expect(hexToOklch(hovered).l).
|
|
342
|
+
expect(hexToOklch(hovered).l).toBeGreaterThanOrEqual(
|
|
343
|
+
hexToOklch(resting).l,
|
|
344
|
+
)
|
|
351
345
|
})
|
|
352
346
|
})
|
|
353
347
|
})
|
|
@@ -5,7 +5,7 @@ import type { AxisChartConfig } from './types'
|
|
|
5
5
|
|
|
6
6
|
const tokens: ChartTokens = {
|
|
7
7
|
categorical: ['#111111', '#222222', '#333333'],
|
|
8
|
-
sequential: ['#000011', '#000022', '#000033'
|
|
8
|
+
sequential: ['#000011', '#000022', '#000033'],
|
|
9
9
|
diverging: ['#001100', '#002200', '#003300'],
|
|
10
10
|
axisLabel: 'ink-5',
|
|
11
11
|
axisTitle: 'ink-7',
|
package/src/charts/style.css
CHANGED
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
The red family sits at hue 17. It was authored at 5, a crimson, whose light
|
|
26
26
|
partner lightens into a pink rather than into a warning — so anything
|
|
27
27
|
reading the ramp for a status had to leave it. 17 keeps the family clear of
|
|
28
|
-
the amber at 75 while its light partner still reads as red.
|
|
28
|
+
the amber at 75 while its light partner still reads as red. Its chroma
|
|
29
|
+
sits at 0.155, under the cap, because the eye weights red above every
|
|
30
|
+
other hue and at the cap a slice of it reads as an alert. Lower still and
|
|
31
|
+
it turns brown. */
|
|
29
32
|
--chart-categorical-1: #2283c3;
|
|
30
33
|
--chart-categorical-2: #84c5f9;
|
|
31
34
|
--chart-categorical-3: #289e60;
|
|
@@ -34,8 +37,8 @@
|
|
|
34
37
|
--chart-categorical-6: #bb9df1;
|
|
35
38
|
--chart-categorical-7: #c98c28;
|
|
36
39
|
--chart-categorical-8: #f5ca8e;
|
|
37
|
-
--chart-categorical-9: #
|
|
38
|
-
--chart-categorical-10: #
|
|
40
|
+
--chart-categorical-9: #c54b58;
|
|
41
|
+
--chart-categorical-10: #fca0a4;
|
|
39
42
|
|
|
40
43
|
--chart-sequential-1: #095895;
|
|
41
44
|
--chart-sequential-2: #2283c3;
|
|
@@ -95,8 +98,8 @@
|
|
|
95
98
|
--chart-categorical-6: #b294e7;
|
|
96
99
|
--chart-categorical-7: #bf8319;
|
|
97
100
|
--chart-categorical-8: #ebc085;
|
|
98
|
-
--chart-categorical-9: #
|
|
99
|
-
--chart-categorical-10: #
|
|
101
|
+
--chart-categorical-9: #ba4250;
|
|
102
|
+
--chart-categorical-10: #f2979b;
|
|
100
103
|
|
|
101
104
|
--chart-sequential-1: #074677;
|
|
102
105
|
--chart-sequential-2: #1b699c;
|
package/src/charts/tokens.ts
CHANGED
|
@@ -56,8 +56,8 @@ const LIGHT_CATEGORICAL = [
|
|
|
56
56
|
'#bb9df1',
|
|
57
57
|
'#c98c28',
|
|
58
58
|
'#f5ca8e',
|
|
59
|
-
'#
|
|
60
|
-
'#
|
|
59
|
+
'#c54b58',
|
|
60
|
+
'#fca0a4',
|
|
61
61
|
]
|
|
62
62
|
|
|
63
63
|
const LIGHT_SEQUENTIAL = [
|
|
@@ -95,8 +95,8 @@ const DARK_CATEGORICAL = [
|
|
|
95
95
|
'#b294e7',
|
|
96
96
|
'#bf8319',
|
|
97
97
|
'#ebc085',
|
|
98
|
-
'#
|
|
99
|
-
'#
|
|
98
|
+
'#ba4250',
|
|
99
|
+
'#f2979b',
|
|
100
100
|
]
|
|
101
101
|
|
|
102
102
|
const DARK_SEQUENTIAL = [
|
|
@@ -277,10 +277,6 @@ export function pickSeriesColor(ramp: string[], index: number) {
|
|
|
277
277
|
return ramp[index % ramp.length]
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
/** The two palest sequential stops vanish against a white card. */
|
|
281
|
-
const SEQUENTIAL_TAIL_TRIM = 2
|
|
282
|
-
/** A lone series reads best as one confident mid-blue, not the ramp's dark end. */
|
|
283
|
-
const SEQUENTIAL_SOLO_INDEX = 1
|
|
284
280
|
/**
|
|
285
281
|
* Below this relative luminance a fill needs white text on top of it. It is the
|
|
286
282
|
* crossover, not a taste call: white and the near-black inside-label ink
|
|
@@ -290,23 +286,57 @@ const SEQUENTIAL_SOLO_INDEX = 1
|
|
|
290
286
|
const DARK_FILL_LUMINANCE = 0.22
|
|
291
287
|
|
|
292
288
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
289
|
+
* How much of the sequential ramp a chart spends before it must widen. The
|
|
290
|
+
* last two stops of a nine-stop ramp are pale enough that a series in them is
|
|
291
|
+
* hard to read against a card, so a chart stays inside the first seven until
|
|
292
|
+
* it has more series than that.
|
|
296
293
|
*/
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
const SEQUENTIAL_SPAN = 7
|
|
295
|
+
/**
|
|
296
|
+
* The most stops apart two neighbouring series sit. Without it two series
|
|
297
|
+
* take the two ends of the span, and a third then recolors the second. With
|
|
298
|
+
* it a chart of one, two or three series is a prefix of the next.
|
|
299
|
+
*/
|
|
300
|
+
const SEQUENTIAL_MAX_STEP = 3
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* `count` stops from the deep end of a sequential ramp: spread evenly over the
|
|
304
|
+
* first `SEQUENTIAL_SPAN` stops, never more than `SEQUENTIAL_MAX_STEP` apart,
|
|
305
|
+
* widening past the span only for a chart with more series than it holds.
|
|
306
|
+
* Cycles once there are more series than stops.
|
|
307
|
+
*/
|
|
308
|
+
function sequentialStops(ramp: string[], count: number): string[] {
|
|
309
|
+
if (count === 1) return [ramp[0]]
|
|
310
|
+
if (count > ramp.length) {
|
|
299
311
|
return Array.from({ length: count }, (_, i) => pickSeriesColor(ramp, i))
|
|
300
312
|
}
|
|
313
|
+
const lastIndex = Math.min(Math.max(SEQUENTIAL_SPAN, count), ramp.length) - 1
|
|
301
314
|
return Array.from(
|
|
302
315
|
{ length: count },
|
|
303
|
-
(_, i) =>
|
|
316
|
+
(_, i) =>
|
|
317
|
+
ramp[
|
|
318
|
+
Math.min(
|
|
319
|
+
Math.round((i * lastIndex) / (count - 1)),
|
|
320
|
+
i * SEQUENTIAL_MAX_STEP,
|
|
321
|
+
)
|
|
322
|
+
],
|
|
304
323
|
)
|
|
305
324
|
}
|
|
306
325
|
|
|
307
|
-
/**
|
|
308
|
-
|
|
309
|
-
|
|
326
|
+
/**
|
|
327
|
+
* `count` evenly spaced stops across the whole of a diverging ramp, which is
|
|
328
|
+
* read by its extremes. Cycles once there are more series than stops.
|
|
329
|
+
*/
|
|
330
|
+
function divergingStops(ramp: string[], count: number): string[] {
|
|
331
|
+
if (count === 1) return [ramp[0]]
|
|
332
|
+
if (count > ramp.length) {
|
|
333
|
+
return Array.from({ length: count }, (_, i) => pickSeriesColor(ramp, i))
|
|
334
|
+
}
|
|
335
|
+
const lastIndex = ramp.length - 1
|
|
336
|
+
return Array.from(
|
|
337
|
+
{ length: count },
|
|
338
|
+
(_, i) => ramp[Math.round((i * lastIndex) / (count - 1))],
|
|
339
|
+
)
|
|
310
340
|
}
|
|
311
341
|
|
|
312
342
|
/**
|
|
@@ -328,16 +358,9 @@ export function paletteColors(
|
|
|
328
358
|
|
|
329
359
|
const ramp = name === 'diverging' ? tokens.diverging : tokens.sequential
|
|
330
360
|
if (!ramp.length) return cycle(tokens.categorical)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return [ramp[Math.min(SEQUENTIAL_SOLO_INDEX, ramp.length - 1)]]
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// A diverging ramp is read by its extremes, so it always spans end to end.
|
|
338
|
-
const lastIndex =
|
|
339
|
-
name === 'diverging' ? ramp.length - 1 : usableSequential(ramp).length - 1
|
|
340
|
-
return rampStops(ramp, count, lastIndex)
|
|
361
|
+
return name === 'diverging'
|
|
362
|
+
? divergingStops(ramp, count)
|
|
363
|
+
: sequentialStops(ramp, count)
|
|
341
364
|
}
|
|
342
365
|
|
|
343
366
|
/**
|
|
@@ -347,10 +370,7 @@ export function paletteColors(
|
|
|
347
370
|
*/
|
|
348
371
|
function namedRamp(name: ChartPaletteName, tokens: ChartTokens): string[] {
|
|
349
372
|
if (name === 'categorical') return tokens.categorical
|
|
350
|
-
const ramp =
|
|
351
|
-
name === 'diverging'
|
|
352
|
-
? tokens.diverging
|
|
353
|
-
: usableSequential(tokens.sequential)
|
|
373
|
+
const ramp = name === 'diverging' ? tokens.diverging : tokens.sequential
|
|
354
374
|
return ramp.length ? ramp : tokens.categorical
|
|
355
375
|
}
|
|
356
376
|
|
|
@@ -17,7 +17,7 @@ describe('Checkbox', () => {
|
|
|
17
17
|
cy.get('input[type="checkbox"]').should('be.disabled')
|
|
18
18
|
// A disabled control dims its label — `disabled` outranks `color="gray-7"`.
|
|
19
19
|
cy.get('label')
|
|
20
|
-
.should('have.class', 'text-
|
|
20
|
+
.should('have.class', 'text-sm')
|
|
21
21
|
.and('have.class', 'text-ink-gray-4')
|
|
22
22
|
.and('not.have.class', 'text-ink-gray-7')
|
|
23
23
|
})
|
|
@@ -28,8 +28,8 @@ describe('Checkbox', () => {
|
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
cy.get('[data-slot="description"]')
|
|
31
|
-
.should('have.class', 'text-ink-gray-
|
|
32
|
-
.and('not.have.class', 'text-ink-gray-
|
|
31
|
+
.should('have.class', 'text-ink-gray-4')
|
|
32
|
+
.and('not.have.class', 'text-ink-gray-6')
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
it('toggles with the space key', () => {
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
name: 'variant',
|
|
31
31
|
description: 'Visual style of the combobox.',
|
|
32
32
|
required: false,
|
|
33
|
-
type: '
|
|
33
|
+
type: 'InputVariant',
|
|
34
34
|
default: '"subtle"'
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
name: 'size',
|
|
38
38
|
description: 'Size of the trigger and option rows.',
|
|
39
39
|
required: false,
|
|
40
|
-
type: '
|
|
40
|
+
type: 'InputSize',
|
|
41
41
|
default: '"sm"'
|
|
42
42
|
},
|
|
43
43
|
{
|
|
@@ -35,10 +35,10 @@ const knobs: Knob[] = [
|
|
|
35
35
|
type: 'tabs',
|
|
36
36
|
default: 'sm',
|
|
37
37
|
options: [
|
|
38
|
+
{ label: 'xs', value: 'xs' },
|
|
38
39
|
{ label: 'sm', value: 'sm' },
|
|
39
40
|
{ label: 'md', value: 'md' },
|
|
40
41
|
{ label: 'lg', value: 'lg' },
|
|
41
|
-
{ label: 'xl', value: 'xl' },
|
|
42
42
|
],
|
|
43
43
|
},
|
|
44
44
|
{
|
|
@@ -88,7 +88,12 @@ const slots = useReactiveSlots<ComboboxSlots>()
|
|
|
88
88
|
const slotFns = slots as Record<string, ((props?: any) => any) | undefined>
|
|
89
89
|
|
|
90
90
|
const model = defineModel<ComboboxOptionValue | null>({ default: null })
|
|
91
|
+
// Documented on `open` in `./types.ts`. A JSDoc block here would be appended
|
|
92
|
+
// to that description by `propsgen`, not replace it.
|
|
91
93
|
const open = defineModel<boolean>('open', { default: false })
|
|
94
|
+
// Optional outside-in control of the search box. Unbound, `defineModel` keeps
|
|
95
|
+
// the value local, so `v-model:query` is never required. Documented on `query`
|
|
96
|
+
// in `./types.ts`.
|
|
92
97
|
const query = defineModel<string>('query', { default: '' })
|
|
93
98
|
// Bound, the query is the consumer's — the component never resets it on its
|
|
94
99
|
// own (see `skipInitialDisplaySync` and the open watcher below).
|
|
@@ -11,7 +11,7 @@ const fields: Field[] = [
|
|
|
11
11
|
label: 'colour',
|
|
12
12
|
options: ['gray', 'blue', 'green', 'red'],
|
|
13
13
|
},
|
|
14
|
-
{ key: 'size', label: 'size', options: ['
|
|
14
|
+
{ key: 'size', label: 'size', options: ['xs', 'sm', 'md', 'lg'] },
|
|
15
15
|
{ key: 'style', label: 'style', options: ['subtle', 'outline', 'ghost'] },
|
|
16
16
|
{
|
|
17
17
|
key: 'fontSize',
|
|
@@ -41,10 +41,10 @@ const colourSwatch: Record<string, string> = {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const sizeDot: Record<string, string> = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
xs: 'size-1.5',
|
|
45
|
+
sm: 'size-2',
|
|
46
|
+
md: 'size-2.5',
|
|
47
|
+
lg: 'size-3',
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const fontSizePx: Record<string, number> = {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { Component, VNodeChild } from 'vue'
|
|
2
|
+
import type { InputSize, InputVariant } from '../../composables/inputTypes'
|
|
2
3
|
import type { InputLabelingProps } from '../../composables/useInputLabeling'
|
|
3
4
|
import type { PortalTarget } from '../../composables/usePortalTarget'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Combobox renders the same trigger geometry and surfaces as every other text
|
|
8
|
+
* input, so these are the input scales under the component's own names. They
|
|
9
|
+
* stay aliases, not copies — a Combobox-only scale would have to come with a
|
|
10
|
+
* Combobox-only renderer.
|
|
11
|
+
*/
|
|
12
|
+
export type ComboboxVariant = InputVariant
|
|
13
|
+
export type ComboboxSize = InputSize
|
|
7
14
|
|
|
8
15
|
import type { PopoverSide, PopoverAlign } from '../shared/selection/types'
|
|
9
16
|
export type { PopoverSide, PopoverAlign }
|
|
@@ -312,12 +319,6 @@ export interface ComboboxEmits {
|
|
|
312
319
|
/** Fired when the committed value changes. */
|
|
313
320
|
'update:modelValue': [value: ComboboxOptionValue | null]
|
|
314
321
|
|
|
315
|
-
/** Fired when the open state changes. */
|
|
316
|
-
'update:open': [value: boolean]
|
|
317
|
-
|
|
318
|
-
/** Fired when the query changes. */
|
|
319
|
-
'update:query': [value: string]
|
|
320
|
-
|
|
321
322
|
/** Fired when the resolved selected option changes. */
|
|
322
323
|
'update:selectedOption': [
|
|
323
324
|
option: ComboboxSelectableOption | ComboboxCustomOption | null,
|