-
Notifications
You must be signed in to change notification settings - Fork 3
/
ButtonGrid.svelte
213 lines (181 loc) · 6.03 KB
/
ButtonGrid.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<script context="module" lang="ts">
export type ButtonGridClickEvent = CustomEvent<{
cell: {
x: number;
y: number;
};
index: number;
label: string;
}>;
</script>
<script lang="ts">
import type { ButtonGridApi as ButtonGridRef } from '@kitschpatrol/tweakpane-plugin-essentials';
import type { ButtonGridBladeParams as ButtonGridOptions } from '@kitschpatrol/tweakpane-plugin-essentials/dist/types/button-grid/plugin.d.ts';
import Blade from '$lib/core/Blade.svelte';
import ClsPad from '$lib/internal/ClsPad.svelte';
import { fillWith } from '$lib/utils';
import { getGridDimensions, type UnwrapCustomEvents } from '$lib/utils.js';
import * as pluginModule from '@kitschpatrol/tweakpane-plugin-essentials';
import { BROWSER } from 'esm-env';
import { type ComponentProps, createEventDispatcher } from 'svelte';
type $$Props = {
/**
* Array of names, each of which will become the title of a button in the grid.
* */
buttons: string[];
/**
* Number of columns to arrange the buttons into.
*
* Setting `columns` without setting `rows` will lock the column count and allow the row
* count to change dynamically based on the number of buttons.
* @default `undefined` \
* Dynamic based on quantity of `buttons`.
* */
columns?: number;
/**
* Text displayed next to the button grid.
* @default `undefined`
*/
label?: string;
/**
* Number of rows to arrange the buttons into.
*
* Setting `rows` without setting `columns` will lock the column count and allow the column
* count to change dynamically based on the number of buttons.
* @default `undefined` \
* Dynamic based on quantity of `buttons`.
* */
rows?: number;
} & Omit<ComponentProps<Blade<ButtonGridOptions, ButtonGridRef>>, 'options' | 'plugin' | 'ref'>;
// Unique
export let columns: $$Props['columns'] = undefined;
export let rows: $$Props['rows'] = undefined;
export let buttons: $$Props['buttons'] = [];
export let label: $$Props['label'] = undefined;
// Seems to be the only way to get event comments to work eslint-disable-next-line
type $$Events = {
/**
* Fires when a button is clicked.
*
* Note that the values described in the `ButtonGridClickEvent` type are available on the
* `event.detail` parameter.
* @event
* */
click: ButtonGridClickEvent;
};
const dispatch = createEventDispatcher<UnwrapCustomEvents<$$Events>>();
let options: ButtonGridOptions;
let gridBlade: ButtonGridRef;
let gridDimensions: { columns: number; rows: number };
function cells(
x: number,
y: number
): {
title: string;
} {
const index = y * gridDimensions.columns + x;
if (index >= 0 && index < buttons.length) {
return {
title: `${buttons[index]}`
};
}
return { title: '' };
}
$: gridDimensions = getGridDimensions(buttons.length, columns, rows);
$: options = {
cells,
label,
size: [gridDimensions.columns, gridDimensions.rows],
view: 'buttongrid'
};
$: gridBlade?.on('click', (event) => {
dispatch('click', {
cell: { x: event.index[0], y: event.index[1] },
index: event.index[1] * gridDimensions.columns + event.index[0],
label: event.cell.title
});
});
</script>
<!--
@component
A grid of `<Button>` components.
Integrates the [Button Grid](https://github.com/tweakpane/plugin-essentials#button-grid) control
from Tweakpane-creator [Hiroki Kokubun's](https://cocopon.me) [Essentials
plugin](https://github.com/tweakpane/plugin-essentials).
See `<RadioGrid>` for a radio-flavored variation.
_Svelte Tweakpane UI_ also includes some additional logic to manage default grid dimensions:
- If no `rows` or `columns` props are provided, it will create a grid with the squarest possible aspect ratio for the given quantity of `values`.
- If a single `rows` or `columns` prop is provided, it lets the undefined axis grow / shrink as needed to accommodate the quantity of `values`.
- If both `rows` _and_ `columns` props area provided, then buttons may be clipped if `rows * columns < values.length`.
Usage outside of a `<Pane>` component will implicitly wrap the button grid in `<Pane
position="inline">`.
Note that _Svelte Tweakpane UI_ embeds a functionally identical [fork](https://github.com/kitschpatrol/tweakpane-plugin-essentials) of the plugin with build optimizations. The fork also changes the package name from `@tweakpane/plugin-essentials` to `@kitschpatrol/tweakpane-plugin-essentials` for consistency with other plugins.
@emits {ButtonGridClickEvent} click - When a button in the grid is clicked.
@example
```svelte
<script lang="ts">
import {
Button,
ButtonGrid,
type ButtonGridClickEvent,
Pane
} from 'svelte-tweakpane-ui';
const keyboard = [
...Array.from(
{
length: 26
},
(_, index) => String.fromCodePoint(65 + index)
),
',',
'.',
'!',
'⌫'
];
let textBuffer = '';
function handleClick(event: ButtonGridClickEvent) {
textBuffer =
event.detail.label === '⌫'
? textBuffer.slice(0, -1)
: textBuffer + event.detail.label;
}
</script>
<Pane position="inline" title="Austerity Keyboard">
<ButtonGrid on:click={handleClick} buttons={keyboard} />
<Button on:click={() => (textBuffer += '\u2002')} title=" " />
</Pane>
<div class="demo">
<p>
{textBuffer}
</p>
</div>
<style>
.demo {
aspect-ratio: 1;
width: 100%;
background: linear-gradient(45deg, orange, magenta);
}
.demo > p {
margin: 0;
padding: 0.5rem;
font-family: monospace;
font-size: 2rem;
line-height: 1.2;
color: white;
word-break: break-all;
white-space: pre-wrap;
}
.demo > p::after {
content: '_';
opacity: 0.5;
}
</style>
```
@sourceLink
[ButtonGrid.svelte](https://github.com/kitschpatrol/svelte-tweakpane-ui/blob/main/src/lib/control/ButtonGrid.svelte)
-->
<Blade bind:ref={gridBlade} {options} plugin={pluginModule} {...$$restProps} />
{#if !BROWSER}
<ClsPad keysAdd={fillWith('containerUnitSize', gridDimensions.rows)} theme={$$props.theme} />
<ClsPad keysAdd={fillWith('containerVerticalPadding', 2)} theme={$$props.theme} />
{/if}