@inglorious/ui 9.1.0 → 9.2.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.
Files changed (37) hide show
  1. package/package.json +2 -2
  2. package/src/controls/button/template.js +13 -12
  3. package/src/controls/button-group/template.js +6 -6
  4. package/src/controls/checkbox/checkbox.stories.js +16 -0
  5. package/src/controls/checkbox/style.css +24 -0
  6. package/src/controls/checkbox/template.js +23 -8
  7. package/src/controls/file-upload/template.js +27 -26
  8. package/src/controls/icon-button/template.js +6 -6
  9. package/src/controls/image-cropper/template.js +3 -1
  10. package/src/controls/radio-group/radio-group.stories.js +19 -0
  11. package/src/controls/radio-group/style.css +30 -0
  12. package/src/controls/radio-group/template.js +27 -15
  13. package/src/controls/select/template.js +5 -6
  14. package/src/controls/slider/template.js +47 -44
  15. package/src/controls/switch/template.js +14 -10
  16. package/src/controls/textarea/template.js +29 -26
  17. package/src/data-display/avatar/template.js +6 -5
  18. package/src/data-display/chip/chip.stories.js +13 -0
  19. package/src/data-display/chip/style.css +22 -0
  20. package/src/data-display/chip/template.js +33 -9
  21. package/src/data-display/data-grid/template.js +1 -1
  22. package/src/data-display/list/template.js +4 -5
  23. package/src/examples/dashboard/primitive-section.js +18 -18
  24. package/src/feedback/alert/template.js +26 -26
  25. package/src/feedback/dialog/template.js +31 -29
  26. package/src/feedback/snackbar/template.js +14 -13
  27. package/src/navigation/bottom-navigation/template.js +11 -12
  28. package/src/navigation/breadcrumbs/template.js +28 -25
  29. package/src/navigation/menu/template.js +9 -10
  30. package/src/navigation/pagination/template.js +15 -18
  31. package/src/navigation/stepper/template.js +5 -5
  32. package/src/navigation/tabs/template.js +2 -1
  33. package/src/surfaces/accordion/template.js +2 -1
  34. package/src/surfaces/card/template.js +3 -2
  35. package/types/controls/checkbox.d.ts +4 -0
  36. package/types/controls/radio-group.d.ts +4 -0
  37. package/types/data-display/chip.d.ts +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/ui",
3
- "version": "9.1.0",
3
+ "version": "9.2.0",
4
4
  "description": "A design system for Inglorious Web with render-function components and customizable themes",
5
5
  "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -315,7 +315,7 @@
315
315
  "@iconify-icons/fa7-regular": "^2.0.0",
316
316
  "@iconify-icons/fa7-solid": "^2.0.0",
317
317
  "@iconify-icons/material-symbols": "^2.0.6",
318
- "@inglorious/charts": "12.0.0",
318
+ "@inglorious/charts": "12.1.0",
319
319
  "@inglorious/eslint-config": "1.2.0",
320
320
  "@inglorious/store": "12.0.0",
321
321
  "@inglorious/vite-plugin-jsx": "2.0.5",
@@ -6,6 +6,7 @@
6
6
  import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
 
@@ -83,18 +84,18 @@ export function render(props) {
83
84
  >
84
85
  ${children}
85
86
  </button>
86
- ${
87
- onFileSelect
88
- ? html`<input
89
- type="file"
90
- accept=${accept ?? ""}
91
- ?multiple=${isMultiple}
92
- ?disabled=${isDisabled}
93
- hidden
94
- @change=${(event) => handleFileChange(event, onFileSelect)}
95
- />`
96
- : nothing
97
- }
87
+ ${when(
88
+ onFileSelect,
89
+ () =>
90
+ html`<input
91
+ type="file"
92
+ accept=${accept ?? ""}
93
+ ?multiple=${isMultiple}
94
+ ?disabled=${isDisabled}
95
+ hidden
96
+ @change=${(event) => handleFileChange(event, onFileSelect)}
97
+ />`,
98
+ )}
98
99
  `
99
100
  }
100
101
 
@@ -6,6 +6,7 @@
6
6
  import { html } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
  import { Button } from "../button/index.js"
@@ -73,13 +74,12 @@ export function render(props) {
73
74
 
74
75
  return Button.render({
75
76
  children: html`
76
- ${icon ? html`<span class="iw-button-icon">${icon}</span>` : null}
77
+ ${when(icon, () => html`<span class="iw-button-icon">${icon}</span>`)}
77
78
  ${children ?? label}
78
- ${
79
- iconAfter
80
- ? html`<span class="iw-button-icon">${iconAfter}</span>`
81
- : null
82
- }
79
+ ${when(
80
+ iconAfter,
81
+ () => html`<span class="iw-button-icon">${iconAfter}</span>`,
82
+ )}
83
83
  `,
84
84
  variant: itemVariant ?? variant,
85
85
  color: itemColor ?? color,
@@ -12,6 +12,12 @@ export default {
12
12
  description: "A line of detail under the label.",
13
13
  },
14
14
  name: { control: "text", description: "Native HTML name attribute." },
15
+ variant: {
16
+ control: "select",
17
+ options: ["default", "card"],
18
+ description:
19
+ "`card` draws the checkbox as a card, outlined while checked.",
20
+ },
15
21
  isChecked: { control: "boolean", description: "Current checked state." },
16
22
  isDisabled: { control: "boolean", description: "Disables interaction." },
17
23
  isRequired: { control: "boolean", description: "Marks input as required." },
@@ -60,3 +66,13 @@ WithDescription.args = {
60
66
  name: "summary",
61
67
  description: "An email every Monday with last week's totals.",
62
68
  }
69
+
70
+ export const Card = {}
71
+ Card.args = {
72
+ ...Default.args,
73
+ label: "Breakfast",
74
+ name: "breakfast",
75
+ description: "Served from 7 to 10, €12",
76
+ variant: "card",
77
+ isChecked: true,
78
+ }
@@ -38,6 +38,30 @@
38
38
  font-size: var(--iw-font-size-lg);
39
39
  }
40
40
 
41
+ .iw-checkbox-media {
42
+ display: block;
43
+ margin-block-end: var(--iw-space-2);
44
+
45
+ img {
46
+ display: block;
47
+ max-inline-size: 100%;
48
+ border-radius: var(--iw-radius-sm);
49
+ }
50
+ }
51
+
52
+ &.iw-checkbox-card {
53
+ display: flex;
54
+ align-items: flex-start;
55
+ padding: var(--iw-space-3);
56
+ border: 1px solid var(--iw-color-border);
57
+ border-radius: var(--iw-radius-md);
58
+
59
+ &:has(:checked) {
60
+ border-color: var(--iw-checkbox-accent);
61
+ background: color-mix(in srgb, var(--iw-checkbox-accent) 8%, transparent);
62
+ }
63
+ }
64
+
41
65
  &.iw-checkbox-disabled {
42
66
  opacity: 0.6;
43
67
  cursor: not-allowed;
@@ -6,6 +6,7 @@
6
6
  import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
 
@@ -22,6 +23,8 @@ export function render(props) {
22
23
  name = "",
23
24
  label = "",
24
25
  description,
26
+ media,
27
+ variant = "default",
25
28
  isChecked = false,
26
29
  isDisabled = false,
27
30
  isRequired = false,
@@ -38,6 +41,7 @@ export function render(props) {
38
41
  const classes = {
39
42
  "iw-checkbox": true,
40
43
  "iw-checkbox-disabled": isDisabled,
44
+ "iw-checkbox-card": variant === "card",
41
45
  [`iw-checkbox-${color}`]: color !== "primary",
42
46
  [`iw-checkbox-${size}`]: size !== "md",
43
47
  }
@@ -55,14 +59,25 @@ export function render(props) {
55
59
  @change=${(event) => onChange?.(event.target.checked)}
56
60
  ${ref((element) => applyElementProps(element, rest))}
57
61
  />
58
- ${
59
- description
60
- ? html`<span class="iw-checkbox-text">
61
- <span class="iw-checkbox-label">${label}</span>
62
- <span class="iw-checkbox-description">${description}</span>
63
- </span>`
64
- : html`<span class="iw-checkbox-label">${label}</span>`
65
- }
62
+ ${when(
63
+ description || media,
64
+ () =>
65
+ html`<span class="iw-checkbox-text">
66
+ ${when(
67
+ media,
68
+ () => html`<span class="iw-checkbox-media">${media}</span>`,
69
+ )}
70
+ <span class="iw-checkbox-label">${label}</span>
71
+ ${when(
72
+ description,
73
+ () =>
74
+ html`<span class="iw-checkbox-description"
75
+ >${description}</span
76
+ >`,
77
+ )}
78
+ </span>`,
79
+ () => html`<span class="iw-checkbox-label">${label}</span>`,
80
+ )}
66
81
  </label>
67
82
  `
68
83
  }
@@ -6,6 +6,7 @@
6
6
  import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
  import { fallbackLabel } from "../../shared/fallback-label.js"
@@ -57,20 +58,18 @@ export function render(props) {
57
58
 
58
59
  return html`
59
60
  <div class=${classMap(wrapperClasses)}>
60
- ${
61
- label
62
- ? html`
63
- <label for=${uploadId || nothing} class="iw-upload-label">
64
- ${label}
65
- ${
66
- isRequired
67
- ? html`<span class="iw-upload-required">*</span>`
68
- : null
69
- }
70
- </label>
71
- `
72
- : null
73
- }
61
+ ${when(
62
+ label,
63
+ () => html`
64
+ <label for=${uploadId || nothing} class="iw-upload-label">
65
+ ${label}
66
+ ${when(
67
+ isRequired,
68
+ () => html`<span class="iw-upload-required">*</span>`,
69
+ )}
70
+ </label>
71
+ `,
72
+ )}
74
73
 
75
74
  <div class=${classMap(fieldClasses)}>
76
75
  <input
@@ -99,19 +98,21 @@ export function render(props) {
99
98
 
100
99
  <div class="iw-upload-field-summary">${summaryText}</div>
101
100
 
102
- ${
103
- error
104
- ? html`<span
105
- id=${messageId || nothing}
106
- class="iw-upload-error-message"
107
- >${error}</span
108
- >`
109
- : hint
110
- ? html`<span id=${messageId || nothing} class="iw-upload-hint"
101
+ ${when(
102
+ error,
103
+ () =>
104
+ html`<span id=${messageId || nothing} class="iw-upload-error-message"
105
+ >${error}</span
106
+ >`,
107
+ () =>
108
+ when(
109
+ hint,
110
+ () =>
111
+ html`<span id=${messageId || nothing} class="iw-upload-hint"
111
112
  >${hint}</span
112
- >`
113
- : null
114
- }
113
+ >`,
114
+ ),
115
+ )}
115
116
  </div>
116
117
  `
117
118
  }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { html } from "@inglorious/web"
7
+ import { when } from "@inglorious/web/directives/when"
7
8
 
8
9
  import { Button } from "../button/index.js"
9
10
 
@@ -25,13 +26,12 @@ export function render(props) {
25
26
 
26
27
  const children = html`
27
28
  <span class="iw-icon-button-content iw-icon-button-content-${direction}">
28
- ${icon ? html`<span class="iw-button-icon">${icon}</span>` : null}
29
+ ${when(icon, () => html`<span class="iw-button-icon">${icon}</span>`)}
29
30
  ${label}
30
- ${
31
- iconAfter
32
- ? html`<span class="iw-button-icon">${iconAfter}</span>`
33
- : null
34
- }
31
+ ${when(
32
+ iconAfter,
33
+ () => html`<span class="iw-button-icon">${iconAfter}</span>`,
34
+ )}
35
35
  </span>
36
36
  `
37
37
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { html } from "@inglorious/web"
8
8
  import { classMap } from "@inglorious/web/directives/class-map"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import {
11
12
  DEFAULT_MIN_SIZE,
@@ -84,7 +85,8 @@ export const ImageCropper = {
84
85
  @keydown=${handleKeyDown(props)}
85
86
  @wheel=${handleWheel(props)}
86
87
  >
87
- ${this.renderImage?.(props)} ${isLoaded ? this.renderFrame?.(props) : ""}
88
+ ${this.renderImage?.(props)}
89
+ ${when(isLoaded, () => this.renderFrame?.(props))}
88
90
  </div>`
89
91
  },
90
92
 
@@ -14,6 +14,11 @@ export default {
14
14
  options: ["column", "row"],
15
15
  description: "Layout direction for options.",
16
16
  },
17
+ variant: {
18
+ control: "select",
19
+ options: ["default", "card"],
20
+ description: "`card` draws each option as a card in a grid.",
21
+ },
17
22
  color: {
18
23
  control: "select",
19
24
  options: ["primary", "secondary", "success", "warning", "error", "info"],
@@ -72,3 +77,17 @@ WithDescriptions.args = {
72
77
  { label: "Express", value: "express", description: "Next day, €9" },
73
78
  ],
74
79
  }
80
+
81
+ export const Cards = {}
82
+ Cards.args = {
83
+ ...Default.args,
84
+ label: "Room",
85
+ name: "room",
86
+ value: "double",
87
+ variant: "card",
88
+ options: [
89
+ { label: "Single", value: "single", description: "One bed, €80" },
90
+ { label: "Double", value: "double", description: "Two beds, €120" },
91
+ { label: "Suite", value: "suite", description: "With a view, €200" },
92
+ ],
93
+ }
@@ -70,4 +70,34 @@
70
70
  &.iw-radio-group-info {
71
71
  --iw-radio-accent: var(--iw-color-info);
72
72
  }
73
+
74
+ /* After the directions, which it overrides with a grid. */
75
+ &.iw-radio-group-card {
76
+ display: grid;
77
+ grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
78
+ gap: var(--iw-space-3);
79
+
80
+ .iw-radio-option {
81
+ align-items: flex-start;
82
+ padding: var(--iw-space-3);
83
+ border: 1px solid var(--iw-color-border);
84
+ border-radius: var(--iw-radius-md);
85
+
86
+ &:has(:checked) {
87
+ border-color: var(--iw-radio-accent);
88
+ background: color-mix(in srgb, var(--iw-radio-accent) 8%, transparent);
89
+ }
90
+ }
91
+ }
92
+
93
+ .iw-radio-media {
94
+ display: block;
95
+ margin-block-end: var(--iw-space-2);
96
+
97
+ img {
98
+ display: block;
99
+ max-inline-size: 100%;
100
+ border-radius: var(--iw-radius-sm);
101
+ }
102
+ }
73
103
  }
@@ -6,6 +6,7 @@
6
6
  import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
 
@@ -24,6 +25,7 @@ export function render(props) {
24
25
  value,
25
26
  options = [],
26
27
  direction = "column",
28
+ variant = "default",
27
29
  color = "primary",
28
30
  isDisabled = false,
29
31
  onChange,
@@ -37,6 +39,7 @@ export function render(props) {
37
39
  const rootClasses = {
38
40
  "iw-radio-group": true,
39
41
  [`iw-radio-group-${direction}`]: true,
42
+ "iw-radio-group-card": variant === "card",
40
43
  [`iw-radio-group-${color}`]: color !== "primary",
41
44
  "iw-radio-group-disabled": isDisabled,
42
45
  }
@@ -47,15 +50,15 @@ export function render(props) {
47
50
  ?disabled=${isDisabled}
48
51
  ${ref((element) => applyElementProps(element, rest))}
49
52
  >
50
- ${
51
- label
52
- ? html`<legend class="iw-radio-group-label">${label}</legend>`
53
- : null
54
- }
53
+ ${when(
54
+ label,
55
+ () => html`<legend class="iw-radio-group-label">${label}</legend>`,
56
+ )}
55
57
  ${options.map((option, index) => {
56
58
  const {
57
59
  label: optionLabel,
58
60
  description: optionDescription,
61
+ media,
59
62
  value: optionValue,
60
63
  isDisabled: optionDisabled = false,
61
64
  ...optionRest
@@ -74,16 +77,25 @@ export function render(props) {
74
77
  @change=${(event) => onChange?.(event.target.value)}
75
78
  ${ref((element) => applyElementProps(element, optionRest))}
76
79
  />
77
- ${
78
- optionDescription
79
- ? html`<span class="iw-radio-text">
80
- <span>${optionLabel}</span>
81
- <span class="iw-radio-description"
82
- >${optionDescription}</span
83
- >
84
- </span>`
85
- : html`<span>${optionLabel}</span>`
86
- }
80
+ ${when(
81
+ optionDescription || media,
82
+ () =>
83
+ html`<span class="iw-radio-text">
84
+ ${when(
85
+ media,
86
+ () => html`<span class="iw-radio-media">${media}</span>`,
87
+ )}
88
+ <span>${optionLabel}</span>
89
+ ${when(
90
+ optionDescription,
91
+ () =>
92
+ html`<span class="iw-radio-description"
93
+ >${optionDescription}</span
94
+ >`,
95
+ )}
96
+ </span>`,
97
+ () => html`<span>${optionLabel}</span>`,
98
+ )}
87
99
  </label>
88
100
  `
89
101
  })}
@@ -52,11 +52,10 @@ export const Select = {
52
52
  () => html`
53
53
  <label for=${id || name || nothing} class="iw-select-label">
54
54
  ${label}
55
- ${
56
- isRequired
57
- ? html`<span class="iw-select-required">*</span>`
58
- : null
59
- }
55
+ ${when(
56
+ isRequired,
57
+ () => html`<span class="iw-select-required">*</span>`,
58
+ )}
60
59
  </label>
61
60
  `,
62
61
  )}
@@ -117,7 +116,7 @@ export const Select = {
117
116
  )
118
117
 
119
118
  return html`<div class="iw-select-frame" @click=${openFromArrowStrip}>
120
- ${icon ? Icon.render({ children: icon, size }) : null}
119
+ ${when(icon, () => Icon.render({ children: icon, size }))}
121
120
 
122
121
  <select
123
122
  id=${controlId || nothing}
@@ -6,6 +6,7 @@
6
6
  import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
  import { fallbackLabel } from "../../shared/fallback-label.js"
@@ -60,51 +61,53 @@ export function render(props) {
60
61
 
61
62
  return html`
62
63
  <div class=${classMap(classes)}>
63
- ${
64
- (label || isValueVisible) &&
65
- html`<div class="iw-slider-header">
66
- ${
67
- label
68
- ? html`<label for=${inputId || nothing}>${label}</label>`
69
- : html`<span></span>`
70
- }
71
- ${
72
- isValueVisible
73
- ? html`<output class="iw-slider-value">${shown}</output>`
74
- : null
75
- }
76
- </div>`
77
- }
78
- ${
79
- isRange
80
- ? renderRange({
81
- inputId,
82
- name,
64
+ ${when(
65
+ label || isValueVisible,
66
+ () =>
67
+ html`<div class="iw-slider-header">
68
+ ${when(
83
69
  label,
84
- value,
85
- min,
86
- max,
87
- step,
88
- isDisabled,
89
- onChange,
90
- rest,
91
- })
92
- : html`<input
93
- id=${inputId || nothing}
94
- class="iw-slider-input"
95
- aria-label=${fallbackLabel(inputId, label)}
96
- type="range"
97
- name=${name}
98
- min=${min}
99
- max=${max}
100
- step=${step}
101
- .value=${String(value)}
102
- ?disabled=${isDisabled}
103
- @input=${(event) => onChange?.(Number(event.target.value))}
104
- ${ref((element) => applyElementProps(element, rest))}
105
- />`
106
- }
107
- ${hasMarks ? renderMarks({ min, max, step }) : nothing}
70
+ () => html`<label for=${inputId || nothing}>${label}</label>`,
71
+ () => html`<span></span>`,
72
+ )}
73
+ ${when(
74
+ isValueVisible,
75
+ () => html`<output class="iw-slider-value">${shown}</output>`,
76
+ )}
77
+ </div>`,
78
+ )}
79
+ ${when(
80
+ isRange,
81
+ () =>
82
+ renderRange({
83
+ inputId,
84
+ name,
85
+ label,
86
+ value,
87
+ min,
88
+ max,
89
+ step,
90
+ isDisabled,
91
+ onChange,
92
+ rest,
93
+ }),
94
+ () =>
95
+ html`<input
96
+ id=${inputId || nothing}
97
+ class="iw-slider-input"
98
+ aria-label=${fallbackLabel(inputId, label)}
99
+ type="range"
100
+ name=${name}
101
+ min=${min}
102
+ max=${max}
103
+ step=${step}
104
+ .value=${String(value)}
105
+ ?disabled=${isDisabled}
106
+ @input=${(event) => onChange?.(Number(event.target.value))}
107
+ ${ref((element) => applyElementProps(element, rest))}
108
+ />`,
109
+ )}
110
+ ${when(hasMarks, () => renderMarks({ min, max, step }))}
108
111
  </div>
109
112
  `
110
113
  }
@@ -6,6 +6,7 @@
6
6
  import { html, nothing } from "@inglorious/web"
7
7
  import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
+ import { when } from "@inglorious/web/directives/when"
9
10
 
10
11
  import { applyElementProps } from "../../shared/apply-element-props.js"
11
12
 
@@ -58,16 +59,19 @@ export function render(props) {
58
59
  <span class="iw-switch-track" aria-hidden="true">
59
60
  <span class="iw-switch-thumb"></span>
60
61
  </span>
61
- ${
62
- description
63
- ? html`<span class="iw-switch-text">
64
- ${label ? html`<span class="iw-switch-label">${label}</span>` : null}
65
- <span class="iw-switch-description">${description}</span>
66
- </span>`
67
- : label
68
- ? html`<span class="iw-switch-label">${label}</span>`
69
- : null
70
- }
62
+ ${when(
63
+ description,
64
+ () =>
65
+ html`<span class="iw-switch-text">
66
+ ${when(label, () => html`<span class="iw-switch-label">${label}</span>`)}
67
+ <span class="iw-switch-description">${description}</span>
68
+ </span>`,
69
+ () =>
70
+ when(
71
+ label,
72
+ () => html`<span class="iw-switch-label">${label}</span>`,
73
+ ),
74
+ )}
71
75
  </label>
72
76
  `
73
77
  }