@timmo001/groundwork-ui 0.6.0 → 0.8.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/custom-elements.json +261 -21
- package/package.json +1 -1
- package/src/fields.ts +32 -6
- package/src/gw-badge.ts +29 -18
- package/src/gw-button.ts +34 -26
- package/src/gw-card.ts +97 -4
- package/src/gw-chip.ts +46 -8
- package/src/gw-input.ts +7 -0
- package/src/gw-menu-button.ts +17 -5
- package/src/gw-segmented.ts +19 -7
- package/src/gw-select.ts +7 -0
- package/src/gw-setting-row.ts +21 -5
- package/src/layout.ts +6 -3
- package/src/styles.ts +26 -0
package/src/gw-menu-button.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { GwElement, css, h } from "./element.js";
|
|
8
|
-
import { base, createIcon, setIconPath } from "./styles.js";
|
|
8
|
+
import { base, controlSizes, createIcon, setIconPath } from "./styles.js";
|
|
9
9
|
|
|
10
10
|
export interface MenuItem {
|
|
11
11
|
readonly value: string;
|
|
@@ -38,9 +38,13 @@ export interface MenuItem {
|
|
|
38
38
|
* @attr {string} empty - What the menu says with no items. Defaults to `Nothing here yet`.
|
|
39
39
|
* @attr {MenuItem[]} items - The items, as JSON.
|
|
40
40
|
* @attr {MenuItem[]} actions - Actions pinned under the items, as JSON.
|
|
41
|
+
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The trigger's size, 24 to 56px tall. Defaults to `medium`.
|
|
41
42
|
*
|
|
42
43
|
* @csspart trigger - The button that opens the menu.
|
|
43
44
|
*
|
|
45
|
+
* @cssprop --gw-menu-button-scale - Scales the trigger, around 1 for medium.
|
|
46
|
+
* @cssprop --gw-control-scale - Scales its height and icon with the theme's density. The theme sets it.
|
|
47
|
+
*
|
|
44
48
|
* @fires {CustomEvent<{ value: string }>} select - When an item or action is chosen, with its `value` in `detail`.
|
|
45
49
|
*/
|
|
46
50
|
export class GwMenuButton extends GwElement {
|
|
@@ -50,10 +54,12 @@ export class GwMenuButton extends GwElement {
|
|
|
50
54
|
empty: {},
|
|
51
55
|
items: { type: "json" },
|
|
52
56
|
actions: { type: "json" },
|
|
57
|
+
size: { reflect: true },
|
|
53
58
|
} as const;
|
|
54
59
|
|
|
55
60
|
static override styles = [
|
|
56
61
|
base,
|
|
62
|
+
controlSizes("menu-button"),
|
|
57
63
|
css`
|
|
58
64
|
:host {
|
|
59
65
|
position: relative;
|
|
@@ -62,14 +68,18 @@ export class GwMenuButton extends GwElement {
|
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
.trigger {
|
|
71
|
+
--gw-size: var(--gw-menu-button-scale, 1);
|
|
72
|
+
--gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1));
|
|
73
|
+
--gw-icon-size: calc(1.25rem * var(--gw-scale));
|
|
74
|
+
|
|
65
75
|
display: inline-flex;
|
|
66
|
-
gap: var(--gw-space-2);
|
|
76
|
+
gap: calc(var(--gw-space-2) * var(--gw-size));
|
|
67
77
|
align-items: center;
|
|
68
78
|
box-sizing: border-box;
|
|
69
|
-
height: 2.5rem;
|
|
70
|
-
padding: 0 var(--gw-space-4);
|
|
79
|
+
height: calc(2.5rem * var(--gw-scale));
|
|
80
|
+
padding: 0 calc(var(--gw-space-4) * var(--gw-size));
|
|
71
81
|
font: inherit;
|
|
72
|
-
font-size: var(--gw-font-size-s);
|
|
82
|
+
font-size: calc(var(--gw-font-size-s) * var(--gw-size));
|
|
73
83
|
font-weight: var(--gw-font-weight-strong);
|
|
74
84
|
color: var(--gw-color-text-strong);
|
|
75
85
|
cursor: pointer;
|
|
@@ -233,6 +243,7 @@ export class GwMenuButton extends GwElement {
|
|
|
233
243
|
declare empty: string;
|
|
234
244
|
declare items: readonly MenuItem[];
|
|
235
245
|
declare actions: readonly MenuItem[];
|
|
246
|
+
declare size: "xs" | "small" | "medium" | "large" | "xl";
|
|
236
247
|
|
|
237
248
|
readonly #trigger = h("button", {
|
|
238
249
|
class: "trigger",
|
|
@@ -264,6 +275,7 @@ export class GwMenuButton extends GwElement {
|
|
|
264
275
|
this.empty = "Nothing here yet";
|
|
265
276
|
this.items = [];
|
|
266
277
|
this.actions = [];
|
|
278
|
+
this.size = "medium";
|
|
267
279
|
}
|
|
268
280
|
|
|
269
281
|
/** The enabled items then actions, in the order arrow keys move through them. */
|
package/src/gw-segmented.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { GwElement, css, h, setAttribute } from "./element.js";
|
|
8
|
-
import { base, createIcon } from "./styles.js";
|
|
8
|
+
import { base, controlSizes, createIcon } from "./styles.js";
|
|
9
9
|
|
|
10
10
|
export interface Segment {
|
|
11
11
|
readonly value: string;
|
|
@@ -31,10 +31,14 @@ export interface Segment {
|
|
|
31
31
|
* @attr {string} value - The chosen option's value.
|
|
32
32
|
* @attr {"show"|"hide"|"auto"} labels - Whether option text shows beside icons. Defaults to `show`.
|
|
33
33
|
* @attr {Segment[]} options - The options, as JSON.
|
|
34
|
+
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The size, 40px tall at medium. Defaults to `medium`.
|
|
34
35
|
*
|
|
35
36
|
* @csspart option - Each option.
|
|
36
37
|
* @csspart indicator - The pill that slides to the chosen option.
|
|
37
38
|
*
|
|
39
|
+
* @cssprop --gw-segmented-scale - Scales the control, around 1 for medium.
|
|
40
|
+
* @cssprop --gw-control-scale - Scales its height and icons with the theme's density. The theme sets it.
|
|
41
|
+
*
|
|
38
42
|
* @fires {Event} change - When the chosen option changes.
|
|
39
43
|
*/
|
|
40
44
|
export class GwSegmented extends GwElement {
|
|
@@ -44,12 +48,17 @@ export class GwSegmented extends GwElement {
|
|
|
44
48
|
value: { reflect: true },
|
|
45
49
|
labels: { reflect: true },
|
|
46
50
|
options: { type: "json" },
|
|
51
|
+
size: { reflect: true },
|
|
47
52
|
} as const;
|
|
48
53
|
|
|
49
54
|
static override styles = [
|
|
50
55
|
base,
|
|
56
|
+
controlSizes("segmented"),
|
|
51
57
|
css`
|
|
52
58
|
:host {
|
|
59
|
+
--gw-size: var(--gw-segmented-scale, 1);
|
|
60
|
+
--gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1));
|
|
61
|
+
|
|
53
62
|
display: inline-block;
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -78,20 +87,21 @@ export class GwSegmented extends GwElement {
|
|
|
78
87
|
label {
|
|
79
88
|
position: relative;
|
|
80
89
|
display: inline-flex;
|
|
81
|
-
gap: var(--gw-space-2);
|
|
90
|
+
gap: calc(var(--gw-space-2) * var(--gw-size));
|
|
82
91
|
align-items: center;
|
|
83
92
|
box-sizing: border-box;
|
|
84
|
-
/* 32px options in a 40px track, matching fields and
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
/* 32px options in a 40px track at medium, matching fields and
|
|
94
|
+
buttons */
|
|
95
|
+
height: calc(2rem * var(--gw-scale));
|
|
96
|
+
padding: 0 calc(var(--gw-space-3) * var(--gw-size));
|
|
97
|
+
font-size: calc(var(--gw-font-size-s) * var(--gw-size));
|
|
88
98
|
color: var(--gw-color-text-primary);
|
|
89
99
|
cursor: pointer;
|
|
90
100
|
background: transparent;
|
|
91
101
|
border-radius: var(--gw-radius-pill);
|
|
92
102
|
transition: background-color var(--gw-motion-fast)
|
|
93
103
|
var(--gw-motion-easing);
|
|
94
|
-
--gw-icon-size: 1.125rem;
|
|
104
|
+
--gw-icon-size: calc(1.125rem * var(--gw-scale));
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
:host([labels="hide"]) label.has-icon {
|
|
@@ -188,6 +198,7 @@ export class GwSegmented extends GwElement {
|
|
|
188
198
|
declare value: string;
|
|
189
199
|
declare labels: "show" | "hide" | "auto";
|
|
190
200
|
declare options: readonly Segment[];
|
|
201
|
+
declare size: "xs" | "small" | "medium" | "large" | "xl";
|
|
191
202
|
|
|
192
203
|
constructor() {
|
|
193
204
|
super();
|
|
@@ -196,6 +207,7 @@ export class GwSegmented extends GwElement {
|
|
|
196
207
|
this.value = "";
|
|
197
208
|
this.labels = "show";
|
|
198
209
|
this.options = [];
|
|
210
|
+
this.size = "medium";
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
readonly #legend = h("legend");
|
package/src/gw-select.ts
CHANGED
|
@@ -62,6 +62,10 @@ const chevronIcon =
|
|
|
62
62
|
* @attr {OptionItem[]} options - The options, as JSON.
|
|
63
63
|
* @attr {boolean} label-hidden - Hides the label but keeps it for assistive technology.
|
|
64
64
|
* @attr {boolean} inline-label - Shows the label inside the trigger, before the value, for toolbars and filters.
|
|
65
|
+
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The field's size, 40px tall at medium. Defaults to `medium`.
|
|
66
|
+
*
|
|
67
|
+
* @cssprop --gw-field-scale - Scales the field, around 1 for medium.
|
|
68
|
+
* @cssprop --gw-control-scale - Scales its height with the theme's density. The theme sets it.
|
|
65
69
|
*
|
|
66
70
|
* @fires {Event} change - When the value, or `values` with `multiple`, changes.
|
|
67
71
|
*/
|
|
@@ -81,6 +85,7 @@ export class GwSelect extends LightElement {
|
|
|
81
85
|
options: { type: "json" },
|
|
82
86
|
labelHidden: { type: "boolean", attribute: "label-hidden" },
|
|
83
87
|
inlineLabel: { type: "boolean", attribute: "inline-label", reflect: true },
|
|
88
|
+
size: { reflect: true },
|
|
84
89
|
} as const;
|
|
85
90
|
|
|
86
91
|
declare label: string;
|
|
@@ -99,6 +104,7 @@ export class GwSelect extends LightElement {
|
|
|
99
104
|
declare options: readonly OptionItem[];
|
|
100
105
|
declare labelHidden: boolean;
|
|
101
106
|
declare inlineLabel: boolean;
|
|
107
|
+
declare size: "xs" | "small" | "medium" | "large" | "xl";
|
|
102
108
|
|
|
103
109
|
readonly #id = fieldId();
|
|
104
110
|
|
|
@@ -172,6 +178,7 @@ export class GwSelect extends LightElement {
|
|
|
172
178
|
this.options = [];
|
|
173
179
|
this.labelHidden = false;
|
|
174
180
|
this.inlineLabel = false;
|
|
181
|
+
this.size = "medium";
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
/** Opens the list, such as from a keyboard shortcut. */
|
package/src/gw-setting-row.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { GwElement, css, h, setAttribute } from "./element.js";
|
|
8
8
|
import "./gw-skeleton.js";
|
|
9
9
|
import { fadeIn } from "./motion.js";
|
|
10
|
-
import { base } from "./styles.js";
|
|
10
|
+
import { base, controlSizes } from "./styles.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* A setting: its name and description on one side, its control on the
|
|
@@ -21,34 +21,42 @@ import { base } from "./styles.js";
|
|
|
21
21
|
* @attr {string} heading - The setting's name.
|
|
22
22
|
* @attr {string} description - What it does.
|
|
23
23
|
* @attr {boolean} loading - Shows skeletons for the control and any unknown text.
|
|
24
|
+
* @attr {"xs"|"small"|"medium"|"large"|"xl"} size - The text and spacing size. Defaults to `medium`.
|
|
24
25
|
*
|
|
25
26
|
* @slot - The control.
|
|
26
27
|
*
|
|
27
28
|
* @csspart row - The row.
|
|
28
29
|
*
|
|
29
30
|
* @cssprop --gw-row-padding - The padding above and below. Defaults to `--gw-space-2`.
|
|
31
|
+
* @cssprop --gw-setting-row-scale - Scales the text and spacing, around 1 for medium.
|
|
30
32
|
*/
|
|
31
33
|
export class GwSettingRow extends GwElement {
|
|
32
34
|
static override properties = {
|
|
33
35
|
heading: {},
|
|
34
36
|
description: {},
|
|
35
37
|
loading: { type: "boolean", reflect: true },
|
|
38
|
+
size: { reflect: true },
|
|
36
39
|
} as const;
|
|
37
40
|
|
|
38
41
|
static override styles = [
|
|
39
42
|
base,
|
|
43
|
+
controlSizes("setting-row"),
|
|
40
44
|
css`
|
|
41
45
|
:host {
|
|
46
|
+
--gw-size: var(--gw-setting-row-scale, 1);
|
|
47
|
+
|
|
42
48
|
display: block;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
.row {
|
|
46
52
|
display: flex;
|
|
47
53
|
flex-wrap: wrap;
|
|
48
|
-
gap: var(--gw-space-1) var(--gw-
|
|
54
|
+
gap: calc(var(--gw-space-1) * var(--gw-size))
|
|
55
|
+
calc(var(--gw-space-3) * var(--gw-size));
|
|
49
56
|
align-items: center;
|
|
50
57
|
justify-content: space-between;
|
|
51
|
-
padding: var(--gw-row-padding, var(--gw-space-2))
|
|
58
|
+
padding: var(--gw-row-padding, calc(var(--gw-space-2) * var(--gw-size)))
|
|
59
|
+
0;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
/* A full line even in themes without borders, since it separates rows */
|
|
@@ -63,13 +71,19 @@ export class GwSettingRow extends GwElement {
|
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
.heading {
|
|
66
|
-
font-size:
|
|
74
|
+
font-size: max(
|
|
75
|
+
var(--gw-font-size-xs),
|
|
76
|
+
calc(var(--gw-font-size-s) * var(--gw-size))
|
|
77
|
+
);
|
|
67
78
|
font-weight: var(--gw-font-weight-strong);
|
|
68
79
|
color: var(--gw-color-text-strong);
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
.description {
|
|
72
|
-
font-size:
|
|
83
|
+
font-size: max(
|
|
84
|
+
var(--gw-font-size-2xs),
|
|
85
|
+
calc(var(--gw-font-size-xs) * var(--gw-size))
|
|
86
|
+
);
|
|
73
87
|
color: var(--gw-color-text-secondary);
|
|
74
88
|
}
|
|
75
89
|
|
|
@@ -89,6 +103,7 @@ export class GwSettingRow extends GwElement {
|
|
|
89
103
|
declare heading: string;
|
|
90
104
|
declare description: string;
|
|
91
105
|
declare loading: boolean;
|
|
106
|
+
declare size: "xs" | "small" | "medium" | "large" | "xl";
|
|
92
107
|
|
|
93
108
|
readonly #row = h("div", { class: "row", part: "row" });
|
|
94
109
|
|
|
@@ -124,6 +139,7 @@ export class GwSettingRow extends GwElement {
|
|
|
124
139
|
this.heading = "";
|
|
125
140
|
this.description = "";
|
|
126
141
|
this.loading = false;
|
|
142
|
+
this.size = "medium";
|
|
127
143
|
}
|
|
128
144
|
|
|
129
145
|
protected override create(root: ShadowRoot) {
|
package/src/layout.ts
CHANGED
|
@@ -36,13 +36,16 @@ export class GwPage extends GwElement {
|
|
|
36
36
|
display: flex;
|
|
37
37
|
flex-direction: column;
|
|
38
38
|
gap: var(--gw-space-6);
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
/* The same box as gw-app-bar's content, so the two line up */
|
|
40
|
+
width: min(100% - 2 * var(--gw-space-3), var(--gw-content-width));
|
|
41
41
|
margin-inline: auto;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
:host([narrow]) .page {
|
|
45
|
-
|
|
45
|
+
width: min(
|
|
46
|
+
100% - 2 * var(--gw-space-3),
|
|
47
|
+
var(--gw-content-width-narrow)
|
|
48
|
+
);
|
|
46
49
|
}
|
|
47
50
|
`,
|
|
48
51
|
];
|
package/src/styles.ts
CHANGED
|
@@ -7,6 +7,31 @@ import { css } from "./element.js";
|
|
|
7
7
|
|
|
8
8
|
const svgNamespace = "http://www.w3.org/2000/svg";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* The `xs` to `xl` presets on a `size` attribute, as `--gw-<name>-scale`
|
|
12
|
+
* around 1 for medium, so an app can also set any size between or beyond
|
|
13
|
+
* them. A control multiplies its height and icon by `--gw-control-scale`
|
|
14
|
+
* too, which follows the theme's density, and its text only by its own
|
|
15
|
+
* scale, since spacing and type already follow density on their own.
|
|
16
|
+
*/
|
|
17
|
+
export const controlSizes = (name: string) => css`
|
|
18
|
+
:host([size="xs"]) {
|
|
19
|
+
--gw-${name}-scale: 0.6;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:host([size="small"]) {
|
|
23
|
+
--gw-${name}-scale: 0.8;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:host([size="large"]) {
|
|
27
|
+
--gw-${name}-scale: 1.2;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:host([size="xl"]) {
|
|
31
|
+
--gw-${name}-scale: 1.4;
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
10
35
|
/**
|
|
11
36
|
* What every component shares: box sizing, the focus ring, icons and the
|
|
12
37
|
* tones that set `--gw-tone` from a `tone` attribute.
|
|
@@ -23,6 +48,7 @@ export const base = css`
|
|
|
23
48
|
box-sizing: border-box;
|
|
24
49
|
}
|
|
25
50
|
|
|
51
|
+
:host([hidden]),
|
|
26
52
|
[hidden] {
|
|
27
53
|
display: none !important;
|
|
28
54
|
}
|