@inglorious/ui 9.8.2 → 9.10.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 (39) hide show
  1. package/package.json +28 -3
  2. package/src/controls/chip-picker/chip-picker.stories.js +22 -0
  3. package/src/controls/chip-picker/index.js +3 -0
  4. package/src/controls/chip-picker/style.css +26 -0
  5. package/src/controls/chip-picker/template.js +53 -0
  6. package/src/controls/combobox/combobox.stories.js +14 -0
  7. package/src/controls/combobox/handlers.js +87 -20
  8. package/src/controls/combobox/helpers.js +50 -4
  9. package/src/controls/combobox/index.js +1 -0
  10. package/src/controls/combobox/template.js +13 -14
  11. package/src/controls/controls.css +3 -2
  12. package/src/data-display/data-display.css +1 -0
  13. package/src/data-display/lightbox/index.js +3 -0
  14. package/src/data-display/lightbox/lightbox.stories.js +25 -0
  15. package/src/data-display/lightbox/style.css +97 -0
  16. package/src/data-display/lightbox/template.js +153 -0
  17. package/src/feedback/confirm-dialog/confirm-dialog.stories.js +33 -0
  18. package/src/feedback/confirm-dialog/index.js +3 -0
  19. package/src/feedback/confirm-dialog/style.css +16 -0
  20. package/src/feedback/confirm-dialog/template.js +70 -0
  21. package/src/feedback/crop-dialog/index.js +3 -0
  22. package/src/feedback/crop-dialog/style.css +43 -0
  23. package/src/feedback/crop-dialog/template.js +82 -0
  24. package/src/feedback/dialog/style.css +2 -1
  25. package/src/feedback/dialog/template.js +5 -2
  26. package/src/feedback/feedback.css +3 -0
  27. package/src/feedback/load-status/index.js +3 -0
  28. package/src/feedback/load-status/load-status.stories.js +16 -0
  29. package/src/feedback/load-status/style.css +10 -0
  30. package/src/feedback/load-status/template.js +43 -0
  31. package/src/index.js +5 -0
  32. package/src/shared/icons.js +12 -0
  33. package/types/controls/chip-picker.d.ts +23 -0
  34. package/types/controls/combobox.d.ts +38 -2
  35. package/types/data-display/lightbox.d.ts +35 -0
  36. package/types/feedback/confirm-dialog.d.ts +34 -0
  37. package/types/feedback/crop-dialog.d.ts +35 -0
  38. package/types/feedback/load-status.d.ts +22 -0
  39. package/types/index.d.ts +17 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/ui",
3
- "version": "9.8.2",
3
+ "version": "9.10.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",
@@ -211,6 +211,31 @@
211
211
  "import": "./src/feedback/backdrop/index.js"
212
212
  },
213
213
  "./backdrop.css": "./src/feedback/backdrop/style.css",
214
+ "./chip-picker": {
215
+ "types": "./types/controls/chip-picker.d.ts",
216
+ "import": "./src/controls/chip-picker/index.js"
217
+ },
218
+ "./chip-picker.css": "./src/controls/chip-picker/style.css",
219
+ "./confirm-dialog": {
220
+ "types": "./types/feedback/confirm-dialog.d.ts",
221
+ "import": "./src/feedback/confirm-dialog/index.js"
222
+ },
223
+ "./confirm-dialog.css": "./src/feedback/confirm-dialog/style.css",
224
+ "./crop-dialog": {
225
+ "types": "./types/feedback/crop-dialog.d.ts",
226
+ "import": "./src/feedback/crop-dialog/index.js"
227
+ },
228
+ "./crop-dialog.css": "./src/feedback/crop-dialog/style.css",
229
+ "./lightbox": {
230
+ "types": "./types/data-display/lightbox.d.ts",
231
+ "import": "./src/data-display/lightbox/index.js"
232
+ },
233
+ "./lightbox.css": "./src/data-display/lightbox/style.css",
234
+ "./load-status": {
235
+ "types": "./types/feedback/load-status.d.ts",
236
+ "import": "./src/feedback/load-status/index.js"
237
+ },
238
+ "./load-status.css": "./src/feedback/load-status/style.css",
214
239
  "./dialog": {
215
240
  "types": "./types/feedback/dialog.d.ts",
216
241
  "import": "./src/feedback/dialog/index.js"
@@ -316,7 +341,7 @@
316
341
  "peerDependencies": {
317
342
  "@inglorious/store": "^12.1.1",
318
343
  "@inglorious/utils": "^5.1.0",
319
- "@inglorious/web": "^6.13.1"
344
+ "@inglorious/web": "^6.14.0"
320
345
  },
321
346
  "devDependencies": {
322
347
  "@iconify-icons/fa7-brands": "^2.0.0",
@@ -328,7 +353,7 @@
328
353
  "@inglorious/store": "12.1.1",
329
354
  "@inglorious/vite-plugin-jsx": "2.0.6",
330
355
  "@inglorious/vite-plugin-vue": "1.0.7",
331
- "@inglorious/web": "6.13.1",
356
+ "@inglorious/web": "6.14.0",
332
357
  "@storybook/addon-docs": "^10.6.5",
333
358
  "@storybook/web-components-vite": "^10.6.5",
334
359
  "happy-dom": "^20.14.0",
@@ -0,0 +1,22 @@
1
+ import { createRender } from "../../stories/notify-story.js"
2
+ import { ChipPicker } from "."
3
+
4
+ export default {
5
+ title: "Controls/ChipPicker",
6
+ tags: ["autodocs"],
7
+ render: createRender(ChipPicker),
8
+ argTypes: { onToggle: { action: "onToggle" } },
9
+ }
10
+
11
+ export const Default = {
12
+ args: {
13
+ label: "What do you do?",
14
+ hint: "Pick as many as you like.",
15
+ options: [
16
+ { value: "photographer", label: "Photographer" },
17
+ { value: "videographer", label: "Videographer" },
18
+ { value: "dj", label: "DJ" },
19
+ ],
20
+ value: ["videographer"],
21
+ },
22
+ }
@@ -0,0 +1,3 @@
1
+ import * as renderers from "./template.js"
2
+
3
+ export const ChipPicker = { ...renderers }
@@ -0,0 +1,26 @@
1
+ .iw-chip-picker {
2
+ min-width: 0;
3
+ margin: 0;
4
+ padding: 0;
5
+ border: none;
6
+ }
7
+
8
+ .iw-chip-picker-legend {
9
+ padding: 0;
10
+ margin-bottom: var(--iw-space-2);
11
+ font-size: var(--iw-font-size-sm);
12
+ font-weight: var(--iw-font-weight-medium);
13
+ color: var(--iw-color-text);
14
+ }
15
+
16
+ .iw-chip-picker-chips {
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: var(--iw-space-2);
20
+ }
21
+
22
+ .iw-chip-picker-hint {
23
+ margin: var(--iw-space-2) 0 0;
24
+ font-size: var(--iw-font-size-xs);
25
+ color: var(--iw-color-text-muted, var(--iw-color-text));
26
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @typedef {import('../../../types/controls/chip-picker').ChipPickerProps} ChipPickerProps
3
+ * @typedef {import('@inglorious/web').TemplateResult} TemplateResult
4
+ */
5
+
6
+ import { html } from "@inglorious/web"
7
+ import { when } from "@inglorious/web/directives/when"
8
+
9
+ import { Chip } from "../../data-display/chip/index.js"
10
+ import { formatOption } from "../combobox/helpers.js"
11
+
12
+ /**
13
+ * Renders a group of toggle chips for picking several options, as a fieldset
14
+ * whose legend is `label`.
15
+ * @param {ChipPickerProps} props
16
+ * @returns {TemplateResult}
17
+ */
18
+ export function render(props) {
19
+ const {
20
+ label,
21
+ hint,
22
+ options = [],
23
+ value = [],
24
+ isDisabled = false,
25
+ className = "",
26
+ onToggle,
27
+ } = props
28
+
29
+ return html`<fieldset
30
+ class=${["iw-chip-picker", className].filter(Boolean).join(" ")}
31
+ >
32
+ ${when(
33
+ label,
34
+ () => html`<legend class="iw-chip-picker-legend">${label}</legend>`,
35
+ )}
36
+ <div class="iw-chip-picker-chips">
37
+ ${options.map((option) => {
38
+ const {
39
+ value: optionValue,
40
+ label: optionLabel,
41
+ ...rest
42
+ } = formatOption(option)
43
+ return Chip.render({
44
+ children: optionLabel,
45
+ isSelected: value.includes(optionValue),
46
+ isDisabled: isDisabled || Boolean(rest.isDisabled),
47
+ onClick: () => onToggle?.(optionValue),
48
+ })
49
+ })}
50
+ </div>
51
+ ${when(hint, () => html`<p class="iw-chip-picker-hint">${hint}</p>`)}
52
+ </fieldset>`
53
+ }
@@ -29,6 +29,10 @@ export default {
29
29
  },
30
30
  isSearchable: { control: "boolean", description: "Show search input." },
31
31
  isClearable: { control: "boolean", description: "Show clear button." },
32
+ isCreatable: {
33
+ control: "boolean",
34
+ description: "Offer to add a searched term that matches no option.",
35
+ },
32
36
  isDisabled: { control: "boolean", description: "Disable the control." },
33
37
  isLoading: { control: "boolean", description: "Show loading state." },
34
38
  searchTerm: { control: "text", description: "Current search filter." },
@@ -95,6 +99,16 @@ MultiOpen.args = {
95
99
  isOpen: true,
96
100
  }
97
101
 
102
+ export const Creatable = {}
103
+ Creatable.args = {
104
+ ...Default.args,
105
+ isMulti: true,
106
+ isCreatable: true,
107
+ selectedValue: ["it", "Atlantis"],
108
+ searchTerm: "Narnia",
109
+ isOpen: true,
110
+ }
111
+
98
112
  export const WithHint = {}
99
113
  WithHint.args = {
100
114
  ...Default.args,
@@ -3,7 +3,9 @@
3
3
  * @typedef {import('../../../types/controls/select.js').SelectOption} SelectOption
4
4
  */
5
5
 
6
- import { filterOptions, formatOption, getOptionValue } from "./helpers.js"
6
+ import { formatOption, getOptionValue, visibleOptions } from "./helpers.js"
7
+
8
+ const NONE = -1
7
9
 
8
10
  /**
9
11
  * Initialize missing select state defaults.
@@ -16,7 +18,7 @@ export function create(entity) {
16
18
 
17
19
  entity.isOpen ??= false
18
20
  entity.searchTerm ??= ""
19
- entity.focusedIndex ??= -1
21
+ entity.focusedIndex ??= NONE
20
22
 
21
23
  entity.isMulti ??= false
22
24
  entity.selectedValue ??= entity.isMulti ? [] : null
@@ -38,15 +40,13 @@ export function create(entity) {
38
40
  export function open(entity) {
39
41
  if (entity.isDisabled) return
40
42
  entity.isOpen = true
41
-
42
- const filtered = filterOptions(entity.options, entity.searchTerm)
43
- entity.focusedIndex = filtered.length ? 0 : -1
43
+ entity.focusedIndex = enabledIndex(visibleOptions(entity), 0, 1)
44
44
  }
45
45
 
46
46
  /** @param {SelectEntity} entity */
47
47
  export function close(entity) {
48
48
  entity.isOpen = false
49
- entity.focusedIndex = -1
49
+ entity.focusedIndex = NONE
50
50
  }
51
51
 
52
52
  /** @param {SelectEntity} entity */
@@ -60,12 +60,18 @@ export function toggle(entity) {
60
60
  * @param {SelectEntity} entity
61
61
  * @param {SelectOption} option
62
62
  */
63
- export function optionSelect(entity, option) {
63
+ export function optionSelect(entity, option, api) {
64
64
  if (entity.isDisabled) return
65
65
 
66
66
  const normalizedOption = formatOption(option)
67
+ if (normalizedOption.isDisabled) return
67
68
  const optionValue = getOptionValue(normalizedOption)
68
69
 
70
+ if (normalizedOption.isCreated) {
71
+ entity.searchTerm = ""
72
+ entity.focusedIndex = NONE
73
+ }
74
+
69
75
  if (entity.isMulti) {
70
76
  const values = Array.isArray(entity.selectedValue)
71
77
  ? entity.selectedValue
@@ -79,17 +85,24 @@ export function optionSelect(entity, option) {
79
85
  }
80
86
 
81
87
  entity.selectedValue = values
88
+ notifyChange(entity, api)
82
89
  return
83
90
  }
84
91
 
85
92
  entity.selectedValue = optionValue
86
93
  close(entity)
94
+ notifyChange(entity, api)
87
95
  }
88
96
 
89
- /** @param {SelectEntity} entity */
90
- export function clear(entity) {
97
+ /**
98
+ * @param {SelectEntity} entity
99
+ * @param {unknown} _payload
100
+ * @param {import('@inglorious/web').Api} [api]
101
+ */
102
+ export function clear(entity, _payload, api) {
91
103
  if (entity.isDisabled) return
92
104
  entity.selectedValue = entity.isMulti ? [] : null
105
+ notifyChange(entity, api)
93
106
  }
94
107
 
95
108
  /**
@@ -98,32 +111,36 @@ export function clear(entity) {
98
111
  */
99
112
  export function searchChange(entity, searchTerm) {
100
113
  entity.searchTerm = searchTerm
101
- const filtered = filterOptions(entity.options, entity.searchTerm)
102
- entity.focusedIndex = filtered.length ? 0 : -1
114
+ entity.focusedIndex = enabledIndex(visibleOptions(entity), 0, 1)
103
115
  }
104
116
 
105
117
  /** @param {SelectEntity} entity */
106
118
  export function focusNext(entity) {
107
- const filtered = filterOptions(entity.options, entity.searchTerm)
108
- if (!filtered.length) return
109
- entity.focusedIndex = Math.min(entity.focusedIndex + 1, filtered.length - 1)
119
+ const next = enabledIndex(visibleOptions(entity), entity.focusedIndex + 1, 1)
120
+ if (next !== NONE) entity.focusedIndex = next
110
121
  }
111
122
 
112
- /** @param {SelectEntity} entity */
123
+ /**
124
+ * Before the first enabled option, focus goes back to the search box.
125
+ * @param {SelectEntity} entity
126
+ */
113
127
  export function focusPrev(entity) {
114
- entity.focusedIndex = Math.max(entity.focusedIndex - 1, -1)
128
+ entity.focusedIndex = enabledIndex(
129
+ visibleOptions(entity),
130
+ entity.focusedIndex - 1,
131
+ -1,
132
+ )
115
133
  }
116
134
 
117
135
  /** @param {SelectEntity} entity */
118
136
  export function focusFirst(entity) {
119
- const filtered = filterOptions(entity.options, entity.searchTerm)
120
- if (filtered.length) entity.focusedIndex = 0
137
+ entity.focusedIndex = enabledIndex(visibleOptions(entity), 0, 1)
121
138
  }
122
139
 
123
140
  /** @param {SelectEntity} entity */
124
141
  export function focusLast(entity) {
125
- const filtered = filterOptions(entity.options, entity.searchTerm)
126
- if (filtered.length) entity.focusedIndex = filtered.length - 1
142
+ const filtered = visibleOptions(entity)
143
+ entity.focusedIndex = enabledIndex(filtered, filtered.length - 1, -1)
127
144
  }
128
145
 
129
146
  /**
@@ -134,3 +151,53 @@ export function focusLast(entity) {
134
151
  export function focusSet(entity, index) {
135
152
  entity.focusedIndex = Number.isInteger(index) ? index : entity.focusedIndex
136
153
  }
154
+
155
+ /**
156
+ * Sets the value from outside, such as a form being filled with saved data.
157
+ * An empty string, as an empty form field holds, is no value. Unlike a
158
+ * choice, it does not notify `changeEvent`.
159
+ * @param {SelectEntity} entity
160
+ * @param {string | number | (string | number)[] | null} value
161
+ */
162
+ export function valueSet(entity, value) {
163
+ if (entity.isMulti) entity.selectedValue = [...(value ?? [])]
164
+ else entity.selectedValue = value === "" ? null : (value ?? null)
165
+ entity.searchTerm = ""
166
+ entity.focusedIndex = NONE
167
+ }
168
+
169
+ /**
170
+ * Replaces the options, such as when they depend on another field.
171
+ * @param {SelectEntity} entity
172
+ * @param {SelectOption[]} options
173
+ */
174
+ export function optionsSet(entity, options) {
175
+ entity.options = (options ?? []).map(formatOption)
176
+ entity.searchTerm = ""
177
+ entity.focusedIndex = NONE
178
+ }
179
+
180
+ /**
181
+ * Shows a validation message. The combobox is its own entity, so the form
182
+ * that validates it hands the message over.
183
+ * @param {SelectEntity} entity
184
+ * @param {string | null} error
185
+ */
186
+ export function errorSet(entity, error) {
187
+ entity.error = error ?? null
188
+ }
189
+
190
+ function notifyChange(entity, api) {
191
+ if (!entity.changeEvent || !api) return
192
+ const value = entity.isMulti
193
+ ? [...entity.selectedValue]
194
+ : entity.selectedValue
195
+ api.notify(entity.changeEvent, { ...entity.changePayload, value })
196
+ }
197
+
198
+ function enabledIndex(options, start, step) {
199
+ for (let index = start; index >= 0 && index < options.length; index += step) {
200
+ if (!formatOption(options[index]).isDisabled) return index
201
+ }
202
+ return NONE
203
+ }
@@ -45,10 +45,45 @@ export function filterOptions(options, searchTerm) {
45
45
 
46
46
  const searchLower = String(searchTerm).toLowerCase().trim()
47
47
 
48
- return options.filter((option) => {
49
- const label = getOptionLabel(option)
50
- return label.toLowerCase().includes(searchLower)
51
- })
48
+ return options.filter((option) =>
49
+ optionTexts(option).some((text) => text.includes(searchLower)),
50
+ )
51
+ }
52
+
53
+ /**
54
+ * The options the dropdown lists: those matching the search, then, on a
55
+ * creatable combobox, one to add the typed term unless it is already known.
56
+ * @param {{ options?: SelectOption[], searchTerm?: string, selectedValue?: string|number|(string|number)[]|null, isCreatable?: boolean, formatCreateLabel?: (term: string) => string }} props
57
+ * @returns {SelectOption[]}
58
+ */
59
+ export function visibleOptions({
60
+ options = [],
61
+ searchTerm,
62
+ selectedValue,
63
+ isCreatable = false,
64
+ formatCreateLabel = (term) => `Add "${term}"`,
65
+ }) {
66
+ const filtered = filterOptions(options, searchTerm)
67
+ const term = String(searchTerm ?? "").trim()
68
+ if (!isCreatable || !term) return filtered
69
+
70
+ const key = term.toLowerCase()
71
+ const isKnown =
72
+ [selectedValue]
73
+ .flat()
74
+ .some((value) => String(value).toLowerCase() === key) ||
75
+ options.some(
76
+ (option) =>
77
+ !formatOption(option).isDisabled &&
78
+ (optionTexts(option).includes(key) ||
79
+ String(getOptionValue(option)).toLowerCase() === key),
80
+ )
81
+ if (isKnown) return filtered
82
+
83
+ return [
84
+ ...filtered,
85
+ { value: term, label: formatCreateLabel(term), isCreated: true },
86
+ ]
52
87
  }
53
88
 
54
89
  /**
@@ -89,3 +124,14 @@ export function formatOption(option) {
89
124
 
90
125
  return { value: String(option), label: String(option) }
91
126
  }
127
+
128
+ /**
129
+ * What a search looks through: the label and the option's keywords, lowercased.
130
+ * @param {SelectOption} option
131
+ * @returns {string[]}
132
+ */
133
+ function optionTexts(option) {
134
+ return [getOptionLabel(option), ...(option?.keywords ?? [])].map((text) =>
135
+ text.toLowerCase(),
136
+ )
137
+ }
@@ -40,4 +40,5 @@ export const {
40
40
  getOptionLabel,
41
41
  getOptionValue,
42
42
  isOptionSelected,
43
+ visibleOptions,
43
44
  } = helpers
@@ -14,11 +14,11 @@ import { Chip } from "../../data-display/chip/index.js"
14
14
  import { fallbackLabel } from "../../shared/fallback-label.js"
15
15
  import { syncPopover } from "../../shared/top-layer.js"
16
16
  import {
17
- filterOptions,
18
17
  formatOption,
19
18
  getOptionLabel,
20
19
  getOptionValue,
21
20
  isOptionSelected,
21
+ visibleOptions,
22
22
  } from "./helpers.js"
23
23
 
24
24
  const NO_FOCUSED_INDEX = -1
@@ -234,18 +234,20 @@ export const Combobox = {
234
234
  */
235
235
  renderMultiValueTag(props, value) {
236
236
  const { id, options, size, onOptionSelect, onChange } = props
237
+ const option = options.find((opt) => getOptionValue(opt) === value) ?? value
237
238
 
238
- const option = options.find((opt) => getOptionValue(opt) === value)
239
239
  const tagId = id ? `${id}-tag-${String(value)}` : undefined
240
240
  const chipSize = size === "lg" ? "md" : "sm"
241
241
 
242
242
  return html`<span
243
243
  class="iw-combobox-multi-value-tag"
244
- @click=${(event) => event.stopPropagation()}
244
+ @click=${(event) => {
245
+ if (event.target.closest(".iw-chip-remove")) event.stopPropagation()
246
+ }}
245
247
  >
246
248
  ${Chip.render({
247
249
  id: tagId,
248
- children: option ? getOptionLabel(option) : String(value),
250
+ children: getOptionLabel(option),
249
251
  isRemovable: true,
250
252
  size: chipSize,
251
253
  shape: "rounded",
@@ -269,9 +271,9 @@ export const Combobox = {
269
271
  * @returns {TemplateResult}
270
272
  */
271
273
  renderDropdown(props) {
272
- const { id, options, searchTerm, isSearchable, isLoading } = props
274
+ const { id, isSearchable, isLoading } = props
273
275
 
274
- const filteredOptions = filterOptions(options, searchTerm)
276
+ const filteredOptions = visibleOptions(props)
275
277
 
276
278
  return html`<div
277
279
  id=${id ? `${id}-control-listbox` : nothing}
@@ -328,9 +330,7 @@ export const Combobox = {
328
330
  * @returns {TemplateResult}
329
331
  */
330
332
  renderOptions(props) {
331
- const { options, searchTerm } = props
332
-
333
- const filteredOptions = filterOptions(options, searchTerm)
333
+ const filteredOptions = visibleOptions(props)
334
334
 
335
335
  return html`<div class="iw-combobox-dropdown-options">
336
336
  ${repeat(
@@ -380,7 +380,7 @@ export const Combobox = {
380
380
  @mouseenter=${() => onFocusSet?.(index)}
381
381
  >
382
382
  ${when(
383
- isMulti,
383
+ isMulti && !normalized.isCreated,
384
384
  () =>
385
385
  html`<input
386
386
  type="checkbox"
@@ -428,7 +428,8 @@ export const Combobox = {
428
428
  function handleControlKeyDown(props, event) {
429
429
  const { isDisabled, isOpen, onToggle } = props
430
430
 
431
- if (isDisabled) return
431
+ // The keys of a tag's remove button or the clear button are their own.
432
+ if (isDisabled || event.target.closest?.("button")) return
432
433
 
433
434
  if (isOpen) {
434
435
  handleKeyDown(props, event)
@@ -450,8 +451,6 @@ function handleControlKeyDown(props, event) {
450
451
  */
451
452
  function handleKeyDown(props, event) {
452
453
  const {
453
- options,
454
- searchTerm,
455
454
  focusedIndex,
456
455
  onFocusFirst,
457
456
  onFocusPrev,
@@ -462,7 +461,7 @@ function handleKeyDown(props, event) {
462
461
  onClose,
463
462
  } = props
464
463
 
465
- const filteredOptions = filterOptions(options, searchTerm)
464
+ const filteredOptions = visibleOptions(props)
466
465
 
467
466
  switch (event.key) {
468
467
  case "ArrowDown":
@@ -1,8 +1,10 @@
1
- @import "./button/style.css";
2
1
  @import "./button-group/style.css";
2
+ @import "./button/style.css";
3
3
  @import "./checkbox/style.css";
4
+ @import "./chip-picker/style.css";
4
5
  @import "./combobox/style.css";
5
6
  @import "./fab/style.css";
7
+ @import "./file-upload/style.css";
6
8
  @import "./icon-button/style.css";
7
9
  @import "./image-cropper/style.css";
8
10
  @import "./input/style.css";
@@ -12,4 +14,3 @@
12
14
  @import "./slider/style.css";
13
15
  @import "./switch/style.css";
14
16
  @import "./textarea/style.css";
15
- @import "./file-upload/style.css";
@@ -7,6 +7,7 @@
7
7
  @import "./data-grid/style.css";
8
8
  @import "./divider/style.css";
9
9
  @import "./icon/style.css";
10
+ @import "./lightbox/style.css";
10
11
  @import "./list/style.css";
11
12
  @import "./table/style.css";
12
13
  @import "./tooltip/style.css";
@@ -0,0 +1,3 @@
1
+ import * as renderers from "./template.js"
2
+
3
+ export const Lightbox = { ...renderers }
@@ -0,0 +1,25 @@
1
+ import { createRender } from "../../stories/notify-story.js"
2
+ import { Lightbox } from "."
3
+
4
+ export default {
5
+ title: "Data Display/Lightbox",
6
+ tags: ["autodocs"],
7
+ render: createRender(Lightbox),
8
+ argTypes: {
9
+ index: { control: "number" },
10
+ onStep: { action: "onStep" },
11
+ onClose: { action: "onClose" },
12
+ },
13
+ }
14
+
15
+ export const Default = {
16
+ args: {
17
+ isOpen: true,
18
+ index: 0,
19
+ images: [
20
+ "https://picsum.photos/id/1011/1200/800",
21
+ "https://picsum.photos/id/1015/1200/800",
22
+ "https://picsum.photos/id/1016/800/1200",
23
+ ],
24
+ },
25
+ }