@inglorious/ui 6.0.0 → 7.0.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 +32 -12
  2. package/src/controls/controls.css +3 -1
  3. package/src/controls/file-upload/file-upload.stories.js +93 -0
  4. package/src/controls/file-upload/index.js +3 -0
  5. package/src/controls/file-upload/style.css +99 -0
  6. package/src/controls/file-upload/template.js +100 -0
  7. package/src/controls/input/input.stories.js +110 -3
  8. package/src/controls/textarea/index.js +3 -0
  9. package/src/controls/textarea/style.css +98 -0
  10. package/src/controls/textarea/template.js +94 -0
  11. package/src/controls/textarea/textarea.stories.js +158 -0
  12. package/src/data-display/before-after/before-after.stories.js +74 -0
  13. package/src/data-display/before-after/handlers.js +44 -0
  14. package/src/data-display/before-after/helpers.js +4 -0
  15. package/src/data-display/before-after/index.js +17 -0
  16. package/src/data-display/before-after/style.css +128 -0
  17. package/src/data-display/before-after/template.js +225 -0
  18. package/src/data-display/carousel/carousel.stories.js +166 -0
  19. package/src/data-display/carousel/handlers.js +78 -0
  20. package/src/data-display/carousel/helpers.js +54 -0
  21. package/src/data-display/carousel/index.js +17 -0
  22. package/src/data-display/carousel/style.css +233 -0
  23. package/src/data-display/carousel/template.js +655 -0
  24. package/src/data-display/data-display.css +2 -0
  25. package/src/examples/dashboard/app-drawer.js +12 -0
  26. package/src/examples/dashboard/before-after-data.js +29 -0
  27. package/src/examples/dashboard/primitive-content.js +220 -33
  28. package/src/index.js +4 -0
  29. package/src/layout/flex/flex.stories.js +49 -0
  30. package/src/layout/flex/style.css +59 -0
  31. package/src/layout/flex/template.js +2 -0
  32. package/types/controls/file-upload.d.ts +27 -0
  33. package/types/controls/input.d.ts +6 -0
  34. package/types/controls/textarea.d.ts +29 -0
  35. package/types/controls/upload-field.d.ts +27 -0
  36. package/types/data-display/before-after.d.ts +39 -0
  37. package/types/data-display/carousel.d.ts +72 -0
  38. package/types/index.d.ts +2 -0
  39. package/types/layout/flex.d.ts +1 -0
@@ -0,0 +1,29 @@
1
+ // Self-contained demo images for the Before/After control, so the dashboard
2
+ // renders the comparison without any network requests.
3
+
4
+ const svg = (label, from, to) =>
5
+ "data:image/svg+xml," +
6
+ encodeURIComponent(
7
+ `<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540">
8
+ <defs>
9
+ <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
10
+ <stop offset="0" stop-color="${from}"/>
11
+ <stop offset="1" stop-color="${to}"/>
12
+ </linearGradient>
13
+ </defs>
14
+ <rect width="960" height="540" fill="url(#g)"/>
15
+ <text x="48" y="496" font-family="sans-serif" font-size="72" font-weight="700"
16
+ fill="rgba(255,255,255,0.92)">${label}</text>
17
+ </svg>`,
18
+ )
19
+
20
+ export const beforeAfterImages = {
21
+ before: {
22
+ src: svg("BEFORE", "#9ca3af", "#374151"),
23
+ alt: "Before — grayscale",
24
+ },
25
+ after: {
26
+ src: svg("AFTER", "#22d3ee", "#4f46e5"),
27
+ alt: "After — color graded",
28
+ },
29
+ }
@@ -1,10 +1,12 @@
1
- import { html } from "@inglorious/web"
1
+ import { createStore } from "@inglorious/store"
2
+ import { html, mount } from "@inglorious/web"
2
3
 
3
4
  import { Button } from "../../controls/button/index.js"
4
5
  import { ButtonGroup } from "../../controls/button-group/index.js"
5
6
  import { Checkbox } from "../../controls/checkbox/index.js"
6
7
  import { Combobox } from "../../controls/combobox/index.js"
7
8
  import { Fab } from "../../controls/fab/index.js"
9
+ import { FileUpload } from "../../controls/file-upload/index.js"
8
10
  import { IconButton } from "../../controls/icon-button/index.js"
9
11
  import { Input } from "../../controls/input/index.js"
10
12
  import { RadioGroup } from "../../controls/radio-group/index.js"
@@ -12,8 +14,11 @@ import { Rating } from "../../controls/rating/index.js"
12
14
  import { Select } from "../../controls/select/index.js"
13
15
  import { Slider } from "../../controls/slider/index.js"
14
16
  import { Switch } from "../../controls/switch/index.js"
17
+ import { Textarea } from "../../controls/textarea/index.js"
15
18
  import { Avatar } from "../../data-display/avatar/index.js"
16
19
  import { Badge } from "../../data-display/badge/index.js"
20
+ import { BeforeAfter } from "../../data-display/before-after/index.js"
21
+ import { Carousel } from "../../data-display/carousel/index.js"
17
22
  import { Chip } from "../../data-display/chip/index.js"
18
23
  import { DataGrid } from "../../data-display/data-grid/index.js"
19
24
  import { Divider } from "../../data-display/divider/index.js"
@@ -45,6 +50,7 @@ import { Accordion } from "../../surfaces/accordion/index.js"
45
50
  import { AppBar } from "../../surfaces/app-bar/index.js"
46
51
  import { Card } from "../../surfaces/card/index.js"
47
52
  import { Paper } from "../../surfaces/paper/index.js"
53
+ import { beforeAfterImages } from "./before-after-data.js"
48
54
 
49
55
  const categoryMeta = {
50
56
  layout: {
@@ -102,6 +108,48 @@ const previewRow = (children) =>
102
108
  const previewColumn = (children) =>
103
109
  html`<div class="iw-primitive-preview-column">${children}</div>`
104
110
 
111
+ const livePreviews = new Map()
112
+
113
+ /**
114
+ * Entity-driven primitives render their controls from state, so a preview
115
+ * without a store freezes: a carousel arrow would recompute from the same page
116
+ * forever, and a dragged divider would never move. Give each one its own store,
117
+ * created once and reused on every re-render so the preview keeps its state.
118
+ * @param {Object.<string, any>} types
119
+ * @param {Object.<string, any>} entity
120
+ * @returns {HTMLElement}
121
+ */
122
+ function livePreview(types, entity) {
123
+ if (!livePreviews.has(entity.id)) {
124
+ const container = document.createElement("div")
125
+ // The mount needs an element, but it must not become a box of its own:
126
+ // as a flex item it would shrink and take the primitive down with it.
127
+ container.style.display = "contents"
128
+
129
+ const store = createStore({ types, entities: { [entity.id]: entity } })
130
+ mount(store, (api) => api.render(entity.id), container)
131
+ livePreviews.set(entity.id, container)
132
+ }
133
+
134
+ return livePreviews.get(entity.id)
135
+ }
136
+
137
+ const carouselSlides = [
138
+ "#22d3ee",
139
+ "#4f46e5",
140
+ "#f59e0b",
141
+ "#10b981",
142
+ "#ef4444",
143
+ ].map(
144
+ (color, index) =>
145
+ html`<div
146
+ style="display:grid;place-items:center;height:14rem;border-radius:0.5rem;
147
+ background:${color};color:#fff;font:700 2rem sans-serif;"
148
+ >
149
+ ${index + 1}
150
+ </div>`,
151
+ )
152
+
105
153
  const primitiveDetailsByPath = {
106
154
  "/layout/container": {
107
155
  summary: "Constrains content width and applies consistent gutters.",
@@ -195,6 +243,38 @@ Grid.render({
195
243
  }),
196
244
  },
197
245
  },
246
+ "/data-display/carousel": {
247
+ summary: "Scroll-snapping carousel with arrows and pagination dots.",
248
+ description:
249
+ "Carousel lays items out in a scroll-snapping viewport: swiping, momentum and snapping come from the browser, while the current page is the only piece of entity state. Arrows and indicators are overridable sub-renders.",
250
+ useCases: ["Image galleries", "Feature tours", "Onboarding slides"],
251
+ example: {
252
+ code: `import { Carousel } from "@inglorious/ui/carousel"
253
+
254
+ Carousel.render({
255
+ id: "demo",
256
+ label: "Example carousel",
257
+ items: [slideOne, slideTwo, slideThree],
258
+ page: 0,
259
+ gap: "md",
260
+ isFullWidth: true
261
+ })`,
262
+ preview: () =>
263
+ livePreview(
264
+ { Carousel },
265
+ {
266
+ id: "carousel",
267
+ type: "Carousel",
268
+ label: "Example carousel",
269
+ items: carouselSlides,
270
+ page: 0,
271
+ gap: "md",
272
+ isInfinite: true,
273
+ isFullWidth: true,
274
+ },
275
+ ),
276
+ },
277
+ },
198
278
  "/controls/button": {
199
279
  summary: "Action trigger with variants, colors, and shapes.",
200
280
  description:
@@ -280,6 +360,36 @@ Checkbox.render({
280
360
  ]),
281
361
  },
282
362
  },
363
+ "/data-display/before-after": {
364
+ name: "Before / After",
365
+ summary: "Drag-to-compare slider for two overlaid images.",
366
+ description:
367
+ "Before/After overlays two images and reveals the top one with a draggable divider. The reveal is pure CSS (clip-path) and the divider position is the only piece of entity state; the divider and handle follow the active theme and color mode.",
368
+ useCases: ["Image comparisons", "Before/after edits", "Visual diffs"],
369
+ example: {
370
+ code: `import { BeforeAfter } from "@inglorious/ui/before-after"
371
+
372
+ BeforeAfter.render({
373
+ id: "demo",
374
+ before: { src: "before.jpg", alt: "Before" },
375
+ after: { src: "after.jpg", alt: "After" },
376
+ position: 50,
377
+ isFullWidth: true
378
+ })`,
379
+ preview: () =>
380
+ livePreview(
381
+ { BeforeAfter },
382
+ {
383
+ id: "beforeAfter",
384
+ type: "BeforeAfter",
385
+ before: beforeAfterImages.before,
386
+ after: beforeAfterImages.after,
387
+ position: 50,
388
+ isFullWidth: true,
389
+ },
390
+ ),
391
+ },
392
+ },
283
393
  "/controls/combobox": {
284
394
  summary: "Searchable dropdown with multi-select and tag support.",
285
395
  description:
@@ -304,21 +414,26 @@ Combobox.render({
304
414
  selectedValue: ["inglorious"]
305
415
  })`,
306
416
  preview: () =>
307
- Combobox.render({
308
- label: "Frameworks",
309
- placeholder: "Select frameworks...",
310
- isMulti: true,
311
- isSearchable: true,
312
- isClearable: true,
313
- options: [
314
- { label: "React", value: "react" },
315
- { label: "Angular", value: "angular" },
316
- { label: "Vue", value: "vue" },
317
- { label: "Svelte", value: "svelte" },
318
- { label: "Inglorious", value: "inglorious" },
319
- ],
320
- selectedValue: ["inglorious"],
321
- }),
417
+ livePreview(
418
+ { Combobox },
419
+ {
420
+ id: "combobox",
421
+ type: "Combobox",
422
+ label: "Frameworks",
423
+ placeholder: "Select frameworks...",
424
+ isMulti: true,
425
+ isSearchable: true,
426
+ isClearable: true,
427
+ options: [
428
+ { label: "React", value: "react" },
429
+ { label: "Angular", value: "angular" },
430
+ { label: "Vue", value: "vue" },
431
+ { label: "Svelte", value: "svelte" },
432
+ { label: "Inglorious", value: "inglorious" },
433
+ ],
434
+ selectedValue: ["inglorious"],
435
+ },
436
+ ),
322
437
  },
323
438
  },
324
439
  "/controls/fab": {
@@ -401,6 +516,73 @@ Input.render({
401
516
  ]),
402
517
  },
403
518
  },
519
+ "/controls/file-upload": {
520
+ summary:
521
+ "File selection area for uploads, attachments, and drag-and-drop flows.",
522
+ description:
523
+ "FileUpload wraps the native file input in a clearer interaction area while preserving validation, hints, and selected file summaries.",
524
+ useCases: ["Attachments", "Bulk upload", "Resume and invoice submissions"],
525
+ example: {
526
+ code: `import { FileUpload } from "@inglorious/ui/file-upload"
527
+
528
+ FileUpload.render({
529
+ label: "Resume",
530
+ accept: ".pdf,.doc,.docx",
531
+ selectedFiles: ["resume.pdf"],
532
+ hint: "PDF or DOCX up to 5MB"
533
+ })`,
534
+ preview: () =>
535
+ previewColumn([
536
+ FileUpload.render({
537
+ label: "Resume",
538
+ accept: ".pdf,.doc,.docx",
539
+ selectedFiles: ["resume.pdf"],
540
+ hint: "PDF or DOCX up to 5MB",
541
+ }),
542
+ FileUpload.render({
543
+ label: "Attachments",
544
+ isMultiple: true,
545
+ selectedFiles: ["brief.pdf", "notes.md"],
546
+ }),
547
+ ]),
548
+ },
549
+ },
550
+ "/controls/textarea": {
551
+ summary: "Multi-line text entry with validation and auto-sizing support.",
552
+ description:
553
+ "Textarea is built for longer responses and keeps its height aligned with the content while supporting helper text and validation states.",
554
+ useCases: [
555
+ "Message bodies",
556
+ "Long-form notes",
557
+ "Descriptions and feedback",
558
+ ],
559
+ example: {
560
+ code: `import { Textarea } from "@inglorious/ui/textarea"
561
+
562
+ Textarea.render({
563
+ label: "Project details",
564
+ placeholder: "Describe the scope and constraints",
565
+ rows: 4,
566
+ hint: "A few sentences are enough"
567
+ })`,
568
+ preview: () =>
569
+ previewColumn([
570
+ Textarea.render({
571
+ label: "Project details",
572
+ placeholder: "Describe the scope and constraints",
573
+ rows: 4,
574
+ hint: "A few sentences are enough",
575
+ value: "",
576
+ }),
577
+ Textarea.render({
578
+ label: "Notes",
579
+ value:
580
+ "The migration is nearly complete and we're validating the edge cases.",
581
+ error: "Please keep it under 280 characters.",
582
+ }),
583
+ ]),
584
+ },
585
+ },
404
586
  "/controls/radio-group": {
405
587
  summary: "Single-choice selection among visible options.",
406
588
  description:
@@ -590,23 +772,28 @@ DataGrid.render({
590
772
  pagination: { page: 0, pageSize: 3, pageSizes: [3, 6] }
591
773
  })`,
592
774
  preview: () =>
593
- DataGrid.render({
594
- rowId: "id",
595
- columns: [
596
- { id: "id", title: "ID", type: "number", isSortable: true },
597
- { id: "name", title: "Name" },
598
- { id: "status", title: "Status" },
599
- ],
600
- rows: [
601
- { id: 1, name: "Alpha", status: "Active" },
602
- { id: 2, name: "Beta", status: "Paused" },
603
- { id: 3, name: "Gamma", status: "Active" },
604
- ],
605
- filters: {},
606
- sorts: [],
607
- selection: [],
608
- pagination: { page: 0, pageSize: 3, pageSizes: [3, 6] },
609
- }),
775
+ livePreview(
776
+ { DataGrid },
777
+ {
778
+ id: "dataGrid",
779
+ type: "DataGrid",
780
+ rowId: "id",
781
+ columns: [
782
+ { id: "id", title: "ID", type: "number", isSortable: true },
783
+ { id: "name", title: "Name" },
784
+ { id: "status", title: "Status" },
785
+ ],
786
+ rows: [
787
+ { id: 1, name: "Alpha", status: "Active" },
788
+ { id: 2, name: "Beta", status: "Paused" },
789
+ { id: 3, name: "Gamma", status: "Active" },
790
+ ],
791
+ filters: {},
792
+ sorts: [],
793
+ selection: [],
794
+ pagination: { page: 0, pageSize: 3, pageSizes: [3, 6] },
795
+ },
796
+ ),
610
797
  },
611
798
  },
612
799
  "/data-display/divider": {
package/src/index.js CHANGED
@@ -9,8 +9,12 @@ export { Rating } from "./controls/rating/index.js"
9
9
  export { Select } from "./controls/select/index.js"
10
10
  export { Slider } from "./controls/slider/index.js"
11
11
  export { Switch } from "./controls/switch/index.js"
12
+ export { Textarea } from "./controls/textarea/index.js"
13
+ export { FileUpload } from "./controls/file-upload/index.js"
12
14
  export { Avatar } from "./data-display/avatar/index.js"
13
15
  export { Badge } from "./data-display/badge/index.js"
16
+ export { BeforeAfter } from "./data-display/before-after/index.js"
17
+ export { Carousel } from "./data-display/carousel/index.js"
14
18
  export { Chip } from "./data-display/chip/index.js"
15
19
  export { DataGrid } from "./data-display/data-grid/index.js"
16
20
  export { Divider } from "./data-display/divider/index.js"
@@ -38,6 +38,12 @@ export default {
38
38
  options: ["none", "sm", "md", "lg", "xl"],
39
39
  description: "Inner padding for the flex container.",
40
40
  },
41
+ overflow: {
42
+ control: "select",
43
+ options: ["visible", "hidden", "clip", "scroll", "auto"],
44
+ description:
45
+ "Overflow behavior. Scrollable values also release flex minimum dimensions.",
46
+ },
41
47
  element: {
42
48
  control: "select",
43
49
  options: [
@@ -84,6 +90,7 @@ Default.args = {
84
90
  align: "stretch",
85
91
  gap: "md",
86
92
  padding: "none",
93
+ overflow: "visible",
87
94
  element: "div",
88
95
  isInline: false,
89
96
  isFullWidth: false,
@@ -119,3 +126,45 @@ Wrapped.args = {
119
126
  html`<div>Six</div>`,
120
127
  ],
121
128
  }
129
+
130
+ export const ScrollableContent = {}
131
+ ScrollableContent.args = {
132
+ direction: "column",
133
+ element: "section",
134
+ overflow: "hidden",
135
+ className: "iw-flex-scrollable-story",
136
+ style: { height: "24rem", border: "1px solid var(--iw-color-border)" },
137
+ children: [
138
+ Flex.render({
139
+ element: "header",
140
+ padding: "md",
141
+ style: {
142
+ flex: "0 0 3.5rem",
143
+ background: "var(--iw-color-surface)",
144
+ },
145
+ children: ["Fixed header"],
146
+ }),
147
+ Flex.render({
148
+ element: "main",
149
+ direction: "column",
150
+ overflow: "auto",
151
+ padding: "md",
152
+ style: { flex: "1 1 0" },
153
+ children: [
154
+ html`<div style="min-height: 48rem">
155
+ This content is intentionally taller than the available space. The
156
+ header and footer stay visible while this main area scrolls.
157
+ </div>`,
158
+ ],
159
+ }),
160
+ Flex.render({
161
+ element: "footer",
162
+ padding: "md",
163
+ style: {
164
+ flex: "0 0 3.5rem",
165
+ background: "var(--iw-color-surface)",
166
+ },
167
+ children: ["Fixed footer"],
168
+ }),
169
+ ],
170
+ }
@@ -120,4 +120,63 @@
120
120
  &.iw-flex-padding-xl {
121
121
  padding: var(--iw-space-8);
122
122
  }
123
+
124
+ /* A flex item needs both minimum dimensions released before either axis can
125
+ overflow instead of expanding its parent. */
126
+ &.iw-flex-overflow-auto,
127
+ &.iw-flex-overflow-scroll,
128
+ &.iw-flex-overflow-hidden,
129
+ &.iw-flex-overflow-clip {
130
+ min-width: 0;
131
+ min-height: 0;
132
+ }
133
+
134
+ &.iw-flex-overflow-auto {
135
+ overflow: auto;
136
+ }
137
+
138
+ &.iw-flex-overflow-scroll {
139
+ overflow: scroll;
140
+ }
141
+
142
+ &.iw-flex-overflow-hidden {
143
+ overflow: hidden;
144
+ }
145
+
146
+ &.iw-flex-overflow-clip {
147
+ overflow: clip;
148
+ }
149
+
150
+ &.iw-flex-overflow-visible {
151
+ overflow: visible;
152
+ }
153
+
154
+ &.iw-flex-overflow-auto,
155
+ &.iw-flex-overflow-scroll {
156
+ scrollbar-color: var(--iw-color-text-muted) var(--iw-color-surface-hover);
157
+ scrollbar-width: thin;
158
+ }
159
+
160
+ &.iw-flex-overflow-auto::-webkit-scrollbar,
161
+ &.iw-flex-overflow-scroll::-webkit-scrollbar {
162
+ width: 0.625rem;
163
+ height: 0.625rem;
164
+ }
165
+
166
+ &.iw-flex-overflow-auto::-webkit-scrollbar-track,
167
+ &.iw-flex-overflow-scroll::-webkit-scrollbar-track {
168
+ background: var(--iw-color-surface-hover);
169
+ }
170
+
171
+ &.iw-flex-overflow-auto::-webkit-scrollbar-thumb,
172
+ &.iw-flex-overflow-scroll::-webkit-scrollbar-thumb {
173
+ border: 0.125rem solid var(--iw-color-surface-hover);
174
+ border-radius: var(--iw-radius-full);
175
+ background: var(--iw-color-text-muted);
176
+ }
177
+
178
+ &.iw-flex-overflow-auto::-webkit-scrollbar-thumb:hover,
179
+ &.iw-flex-overflow-scroll::-webkit-scrollbar-thumb:hover {
180
+ background: var(--iw-color-text-secondary);
181
+ }
123
182
  }
@@ -24,6 +24,7 @@ export function render(props) {
24
24
  align = "stretch",
25
25
  gap = "none",
26
26
  padding = "none",
27
+ overflow = "visible",
27
28
  isInline = false,
28
29
  isFullWidth = false,
29
30
  className = "",
@@ -49,6 +50,7 @@ export function render(props) {
49
50
  [`iw-flex-align-${align}`]: true,
50
51
  [`iw-flex-gap-${gap}`]: true,
51
52
  [`iw-flex-padding-${padding}`]: true,
53
+ [`iw-flex-overflow-${overflow}`]: true,
52
54
  ...extraClasses,
53
55
  }
54
56
 
@@ -0,0 +1,27 @@
1
+ import type { TemplateResult } from "@inglorious/web"
2
+
3
+ export interface FileUploadProps {
4
+ id?: string
5
+ name?: string
6
+ label?: string
7
+ hint?: string
8
+ error?: string
9
+ accept?: string
10
+ isMultiple?: boolean
11
+ isDisabled?: boolean
12
+ isRequired?: boolean
13
+ isFullWidth?: boolean
14
+ selectedFiles?: string[]
15
+ title?: string
16
+ description?: string
17
+ onChange?: (files: FileList | null) => void
18
+ onBlur?: () => void
19
+ onFocus?: () => void
20
+ [key: string]: unknown
21
+ }
22
+
23
+ export interface FileUploadType {
24
+ render(props: FileUploadProps): TemplateResult
25
+ }
26
+
27
+ export declare const FileUpload: FileUploadType
@@ -11,6 +11,12 @@ export interface InputProps {
11
11
  | "tel"
12
12
  | "url"
13
13
  | "search"
14
+ | "date"
15
+ | "time"
16
+ | "datetime-local"
17
+ | "month"
18
+ | "week"
19
+ | "color"
14
20
  value?: string
15
21
  placeholder?: string
16
22
  label?: string
@@ -0,0 +1,29 @@
1
+ import type { TemplateResult } from "@inglorious/web"
2
+
3
+ export interface TextareaProps {
4
+ id?: string
5
+ name?: string
6
+ value?: string
7
+ placeholder?: string
8
+ label?: string
9
+ hint?: string
10
+ error?: string
11
+ size?: "sm" | "md" | "lg"
12
+ rows?: number
13
+ isDisabled?: boolean
14
+ isReadOnly?: boolean
15
+ isRequired?: boolean
16
+ isFullWidth?: boolean
17
+ isAutoSize?: boolean
18
+ isResizable?: boolean
19
+ onChange?: (value: string) => void
20
+ onBlur?: () => void
21
+ onFocus?: () => void
22
+ [key: string]: unknown
23
+ }
24
+
25
+ export interface TextareaType {
26
+ render(props: TextareaProps): TemplateResult
27
+ }
28
+
29
+ export declare const Textarea: TextareaType
@@ -0,0 +1,27 @@
1
+ import type { TemplateResult } from "@inglorious/web"
2
+
3
+ export interface FileUploadProps {
4
+ id?: string
5
+ name?: string
6
+ label?: string
7
+ hint?: string
8
+ error?: string
9
+ accept?: string
10
+ multiple?: boolean
11
+ isDisabled?: boolean
12
+ isRequired?: boolean
13
+ isFullWidth?: boolean
14
+ selectedFiles?: string[]
15
+ title?: string
16
+ description?: string
17
+ onChange?: (files: FileList | null) => void
18
+ onBlur?: () => void
19
+ onFocus?: () => void
20
+ [key: string]: unknown
21
+ }
22
+
23
+ export interface FileUploadType {
24
+ render(props: FileUploadProps): TemplateResult
25
+ }
26
+
27
+ export declare const FileUpload: FileUploadType
@@ -0,0 +1,39 @@
1
+ import type { TemplateResult, Api } from "@inglorious/web"
2
+
3
+ export interface BeforeAfterImage {
4
+ src: string
5
+ alt?: string
6
+ }
7
+
8
+ export interface BeforeAfterProps {
9
+ id?: string
10
+ type?: string
11
+ label?: string
12
+
13
+ before?: BeforeAfterImage | null
14
+ after?: BeforeAfterImage | null
15
+
16
+ /** Divider location as a percentage (0-100). The only persistent state. */
17
+ position?: number
18
+ /** Keyboard increment, in percent, for arrow keys. */
19
+ step?: number
20
+
21
+ isDisabled?: boolean
22
+ isFullWidth?: boolean
23
+
24
+ onPositionChange?: (position: number) => void
25
+ [key: string]: unknown
26
+ }
27
+
28
+ export interface BeforeAfterType {
29
+ create(entity: BeforeAfterProps): void
30
+ positionChange(entity: BeforeAfterProps, position: number): void
31
+ render(entity: BeforeAfterProps, api: Api): TemplateResult
32
+ renderBeforeAfter(props: BeforeAfterProps): TemplateResult
33
+ renderBefore(props: BeforeAfterProps): TemplateResult
34
+ renderAfter(props: BeforeAfterProps): TemplateResult
35
+ renderDivider(props: BeforeAfterProps): TemplateResult
36
+ renderHandle(props: BeforeAfterProps): TemplateResult
37
+ }
38
+
39
+ export declare const BeforeAfter: BeforeAfterType