@inglorious/ui 6.1.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.
- package/package.json +16 -6
- package/src/controls/controls.css +2 -0
- package/src/controls/file-upload/file-upload.stories.js +93 -0
- package/src/controls/file-upload/index.js +3 -0
- package/src/controls/file-upload/style.css +99 -0
- package/src/controls/file-upload/template.js +100 -0
- package/src/controls/input/input.stories.js +110 -3
- package/src/controls/textarea/index.js +3 -0
- package/src/controls/textarea/style.css +98 -0
- package/src/controls/textarea/template.js +94 -0
- package/src/controls/textarea/textarea.stories.js +158 -0
- package/src/examples/dashboard/app-drawer.js +6 -0
- package/src/examples/dashboard/primitive-content.js +69 -0
- package/src/index.js +2 -0
- package/types/controls/file-upload.d.ts +27 -0
- package/types/controls/input.d.ts +6 -0
- package/types/controls/textarea.d.ts +29 -0
- package/types/controls/upload-field.d.ts +27 -0
- package/types/index.d.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.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",
|
|
@@ -113,6 +113,16 @@
|
|
|
113
113
|
"import": "./src/controls/switch/index.js"
|
|
114
114
|
},
|
|
115
115
|
"./switch.css": "./src/controls/switch/style.css",
|
|
116
|
+
"./textarea": {
|
|
117
|
+
"types": "./types/controls/textarea.d.ts",
|
|
118
|
+
"import": "./src/controls/textarea/index.js"
|
|
119
|
+
},
|
|
120
|
+
"./textarea.css": "./src/controls/textarea/style.css",
|
|
121
|
+
"./file-upload": {
|
|
122
|
+
"types": "./types/controls/file-upload.d.ts",
|
|
123
|
+
"import": "./src/controls/file-upload/index.js"
|
|
124
|
+
},
|
|
125
|
+
"./file-upload.css": "./src/controls/file-upload/style.css",
|
|
116
126
|
"./card": {
|
|
117
127
|
"types": "./types/surfaces/card.d.ts",
|
|
118
128
|
"import": "./src/surfaces/card/index.js"
|
|
@@ -290,10 +300,10 @@
|
|
|
290
300
|
"**/*.css"
|
|
291
301
|
],
|
|
292
302
|
"peerDependencies": {
|
|
303
|
+
"@inglorious/charts": "10.0.0",
|
|
293
304
|
"@inglorious/store": "11.0.0",
|
|
294
|
-
"@inglorious/
|
|
295
|
-
"@inglorious/
|
|
296
|
-
"@inglorious/web": "6.3.0"
|
|
305
|
+
"@inglorious/web": "6.4.0",
|
|
306
|
+
"@inglorious/utils": "3.8.0"
|
|
297
307
|
},
|
|
298
308
|
"devDependencies": {
|
|
299
309
|
"@fontsource/material-symbols-outlined": "^5.2.36",
|
|
@@ -308,10 +318,10 @@
|
|
|
308
318
|
"vite": "^7.3.1",
|
|
309
319
|
"vitest": "^4.0.15",
|
|
310
320
|
"@inglorious/eslint-config": "1.1.2",
|
|
321
|
+
"@inglorious/web": "6.4.0",
|
|
311
322
|
"@inglorious/vite-plugin-vue": "1.0.1",
|
|
312
323
|
"@inglorious/vite-plugin-jsx": "2.0.0",
|
|
313
|
-
"@inglorious/store": "11.0.0"
|
|
314
|
-
"@inglorious/web": "6.3.0"
|
|
324
|
+
"@inglorious/store": "11.0.0"
|
|
315
325
|
},
|
|
316
326
|
"engines": {
|
|
317
327
|
"node": ">= 22"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createRender } from "../../stories/notifyStory.js"
|
|
2
|
+
import { FileUpload } from "./index.js"
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Controls/FileUpload",
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
render: createRender(FileUpload),
|
|
8
|
+
argTypes: {
|
|
9
|
+
label: {
|
|
10
|
+
control: "text",
|
|
11
|
+
description: "Field label rendered above the upload area.",
|
|
12
|
+
},
|
|
13
|
+
name: {
|
|
14
|
+
control: "text",
|
|
15
|
+
description: "Native HTML file input name attribute.",
|
|
16
|
+
},
|
|
17
|
+
accept: {
|
|
18
|
+
control: "text",
|
|
19
|
+
description: "Accepted file types, such as .pdf,.png,image/*.",
|
|
20
|
+
},
|
|
21
|
+
isMultiple: {
|
|
22
|
+
control: "boolean",
|
|
23
|
+
description: "Allows selecting multiple files.",
|
|
24
|
+
},
|
|
25
|
+
hint: {
|
|
26
|
+
control: "text",
|
|
27
|
+
description: "Helper text shown below the field when there is no error.",
|
|
28
|
+
},
|
|
29
|
+
error: {
|
|
30
|
+
control: "text",
|
|
31
|
+
description: "Validation message shown below the field.",
|
|
32
|
+
},
|
|
33
|
+
isDisabled: {
|
|
34
|
+
control: "boolean",
|
|
35
|
+
description: "Disables user input and interaction.",
|
|
36
|
+
},
|
|
37
|
+
isRequired: {
|
|
38
|
+
control: "boolean",
|
|
39
|
+
description: "Marks the field as required.",
|
|
40
|
+
},
|
|
41
|
+
isFullWidth: {
|
|
42
|
+
control: "boolean",
|
|
43
|
+
description: "Expands the upload area width to 100% of its container.",
|
|
44
|
+
},
|
|
45
|
+
selectedFiles: {
|
|
46
|
+
control: "object",
|
|
47
|
+
description: "Current list of selected file names for display.",
|
|
48
|
+
},
|
|
49
|
+
onChange: { action: "onChange" },
|
|
50
|
+
onBlur: { action: "onBlur" },
|
|
51
|
+
onFocus: { action: "onFocus" },
|
|
52
|
+
},
|
|
53
|
+
parameters: {
|
|
54
|
+
docs: {
|
|
55
|
+
description: {
|
|
56
|
+
component:
|
|
57
|
+
"Upload area for files with native input semantics, validation, and a clean drag-and-drop style.",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const Default = {}
|
|
64
|
+
Default.args = {
|
|
65
|
+
label: "Resume",
|
|
66
|
+
name: "resume",
|
|
67
|
+
accept: ".pdf,.doc,.docx",
|
|
68
|
+
isMultiple: false,
|
|
69
|
+
hint: "Upload your resume in PDF or DOC format.",
|
|
70
|
+
error: "",
|
|
71
|
+
isDisabled: false,
|
|
72
|
+
isRequired: false,
|
|
73
|
+
isFullWidth: false,
|
|
74
|
+
selectedFiles: [],
|
|
75
|
+
title: "Choose file",
|
|
76
|
+
description: "or drag and drop here",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const Selected = {}
|
|
80
|
+
Selected.args = {
|
|
81
|
+
...Default.args,
|
|
82
|
+
label: "Attachments",
|
|
83
|
+
isMultiple: true,
|
|
84
|
+
selectedFiles: ["brief.pdf", "notes.md"],
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const Error = {}
|
|
88
|
+
Error.args = {
|
|
89
|
+
...Default.args,
|
|
90
|
+
label: "Invoice",
|
|
91
|
+
error: "Only PDF files under 5MB are allowed.",
|
|
92
|
+
selectedFiles: ["invoice.png"],
|
|
93
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
.iw-upload {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--iw-space-1);
|
|
5
|
+
font-family: var(--iw-font-family-sans);
|
|
6
|
+
|
|
7
|
+
&.iw-upload-full-width .iw-upload-field-box {
|
|
8
|
+
width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.iw-upload-label {
|
|
12
|
+
font-size: var(--iw-font-size-sm);
|
|
13
|
+
font-weight: var(--iw-font-weight-normal);
|
|
14
|
+
color: var(--iw-color-text);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.iw-upload-required {
|
|
18
|
+
color: var(--iw-color-error);
|
|
19
|
+
margin-left: var(--iw-space-1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.iw-upload-field-box {
|
|
23
|
+
position: relative;
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
min-height: 7rem;
|
|
28
|
+
padding: var(--iw-space-4);
|
|
29
|
+
border: 1px dashed var(--iw-input-border);
|
|
30
|
+
border-radius: var(--iw-input-radius);
|
|
31
|
+
background: var(--iw-input-bg);
|
|
32
|
+
color: var(--iw-color-text);
|
|
33
|
+
transition: all var(--iw-transition-fast);
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
|
|
36
|
+
&:hover:not(:disabled) {
|
|
37
|
+
background: var(--iw-input-bg-hover);
|
|
38
|
+
border-color: var(--iw-input-border-hover);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:focus-within {
|
|
42
|
+
border-color: var(--iw-input-border-focus);
|
|
43
|
+
box-shadow: var(--iw-input-shadow-focus);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&.iw-upload-field-error {
|
|
47
|
+
border-color: var(--iw-color-error);
|
|
48
|
+
|
|
49
|
+
&:focus-within {
|
|
50
|
+
border-color: var(--iw-color-error);
|
|
51
|
+
box-shadow: 0 0 0 1px var(--iw-color-error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.iw-upload-input {
|
|
57
|
+
position: absolute;
|
|
58
|
+
inset: 0;
|
|
59
|
+
opacity: 0;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.iw-upload-content {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: var(--iw-space-1);
|
|
68
|
+
pointer-events: none;
|
|
69
|
+
text-align: center;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.iw-upload-title {
|
|
73
|
+
font-weight: var(--iw-font-weight-medium);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.iw-upload-description {
|
|
77
|
+
color: var(--iw-color-text-muted);
|
|
78
|
+
font-size: var(--iw-font-size-sm);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.iw-upload-field-summary {
|
|
82
|
+
font-size: var(--iw-font-size-sm);
|
|
83
|
+
color: var(--iw-color-text-muted);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.iw-upload-hint,
|
|
87
|
+
.iw-upload-error-message {
|
|
88
|
+
font-size: var(--iw-font-size-sm);
|
|
89
|
+
line-height: var(--iw-line-height-normal);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.iw-upload-hint {
|
|
93
|
+
color: var(--iw-color-text-muted);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.iw-upload-error-message {
|
|
97
|
+
color: var(--iw-color-error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/controls/file-upload').FileUploadProps} FileUploadProps
|
|
3
|
+
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { html } from "@inglorious/web"
|
|
7
|
+
import { classMap } from "@inglorious/web/directives/class-map"
|
|
8
|
+
import { ref } from "@inglorious/web/directives/ref"
|
|
9
|
+
|
|
10
|
+
import { applyElementProps } from "../../shared/applyElementProps.js"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Renders a labeled file upload area with optional summary, hint, and error text.
|
|
14
|
+
* Supports drag-and-drop handling through file input semantics.
|
|
15
|
+
* @param {FileUploadProps} props
|
|
16
|
+
* @returns {TemplateResult}
|
|
17
|
+
*/
|
|
18
|
+
export function render(props) {
|
|
19
|
+
const {
|
|
20
|
+
id,
|
|
21
|
+
name = "",
|
|
22
|
+
label,
|
|
23
|
+
hint,
|
|
24
|
+
error,
|
|
25
|
+
accept,
|
|
26
|
+
isMultiple = false,
|
|
27
|
+
isDisabled = false,
|
|
28
|
+
isRequired = false,
|
|
29
|
+
isFullWidth = false,
|
|
30
|
+
selectedFiles = [],
|
|
31
|
+
title = "Choose files",
|
|
32
|
+
description = "or drag and drop here",
|
|
33
|
+
onChange,
|
|
34
|
+
onBlur,
|
|
35
|
+
onFocus,
|
|
36
|
+
...rest
|
|
37
|
+
} = props
|
|
38
|
+
|
|
39
|
+
const uploadId = id || name || "file-upload"
|
|
40
|
+
|
|
41
|
+
const wrapperClasses = {
|
|
42
|
+
"iw-upload": true,
|
|
43
|
+
"iw-upload-full-width": isFullWidth,
|
|
44
|
+
"iw-upload-disabled": isDisabled,
|
|
45
|
+
"iw-upload-has-error": !!error,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const fieldClasses = {
|
|
49
|
+
"iw-upload-field-box": true,
|
|
50
|
+
"iw-upload-field-error": !!error,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const summaryText =
|
|
54
|
+
selectedFiles.length > 0 ? selectedFiles.join(", ") : "No file selected"
|
|
55
|
+
|
|
56
|
+
return html`
|
|
57
|
+
<div class=${classMap(wrapperClasses)}>
|
|
58
|
+
${label
|
|
59
|
+
? html`
|
|
60
|
+
<label for=${uploadId} class="iw-upload-label">
|
|
61
|
+
${label}
|
|
62
|
+
${isRequired
|
|
63
|
+
? html`<span class="iw-upload-required">*</span>`
|
|
64
|
+
: null}
|
|
65
|
+
</label>
|
|
66
|
+
`
|
|
67
|
+
: null}
|
|
68
|
+
|
|
69
|
+
<div class=${classMap(fieldClasses)}>
|
|
70
|
+
<input
|
|
71
|
+
id=${uploadId}
|
|
72
|
+
name=${name}
|
|
73
|
+
type="file"
|
|
74
|
+
accept=${accept ?? ""}
|
|
75
|
+
?multiple=${isMultiple}
|
|
76
|
+
?disabled=${isDisabled}
|
|
77
|
+
?required=${isRequired}
|
|
78
|
+
class="iw-upload-input"
|
|
79
|
+
@change=${(event) => onChange?.(event.target.files)}
|
|
80
|
+
@blur=${onBlur}
|
|
81
|
+
@focus=${onFocus}
|
|
82
|
+
${ref((element) => applyElementProps(element, rest))}
|
|
83
|
+
/>
|
|
84
|
+
|
|
85
|
+
<div class="iw-upload-content">
|
|
86
|
+
<span class="iw-upload-title">${title}</span>
|
|
87
|
+
<span class="iw-upload-description">${description}</span>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="iw-upload-field-summary">${summaryText}</div>
|
|
92
|
+
|
|
93
|
+
${error
|
|
94
|
+
? html`<span class="iw-upload-error-message">${error}</span>`
|
|
95
|
+
: hint
|
|
96
|
+
? html`<span class="iw-upload-hint">${hint}</span>`
|
|
97
|
+
: null}
|
|
98
|
+
</div>
|
|
99
|
+
`
|
|
100
|
+
}
|
|
@@ -1,12 +1,45 @@
|
|
|
1
|
+
import { html } from "@inglorious/web"
|
|
2
|
+
|
|
1
3
|
import { createRender } from "../../stories/notifyStory.js"
|
|
2
4
|
import { Input } from "."
|
|
3
5
|
|
|
6
|
+
const nativeInputTypes = [
|
|
7
|
+
"text",
|
|
8
|
+
"password",
|
|
9
|
+
"email",
|
|
10
|
+
"number",
|
|
11
|
+
"tel",
|
|
12
|
+
"url",
|
|
13
|
+
"search",
|
|
14
|
+
"date",
|
|
15
|
+
"time",
|
|
16
|
+
"datetime-local",
|
|
17
|
+
"month",
|
|
18
|
+
"week",
|
|
19
|
+
"color",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const nativeInputDefaults = {
|
|
23
|
+
text: { label: "Text", value: "Hello world" },
|
|
24
|
+
password: { label: "Password", value: "secret123" },
|
|
25
|
+
email: { label: "Email", value: "you@example.com" },
|
|
26
|
+
number: { label: "Number", value: "42" },
|
|
27
|
+
tel: { label: "Phone", value: "+1 555 123 4567" },
|
|
28
|
+
url: { label: "Website", value: "https://example.com" },
|
|
29
|
+
search: { label: "Search", value: "inglorious" },
|
|
30
|
+
date: { label: "Date", value: "2026-08-10" },
|
|
31
|
+
time: { label: "Time", value: "09:30" },
|
|
32
|
+
"datetime-local": { label: "Date and time", value: "2026-08-10T09:30" },
|
|
33
|
+
month: { label: "Month", value: "2026-08" },
|
|
34
|
+
week: { label: "Week", value: "2026-W32" },
|
|
35
|
+
color: { label: "Color", value: "#7c3aed" },
|
|
36
|
+
}
|
|
37
|
+
|
|
4
38
|
export default {
|
|
5
39
|
title: "Controls/Input",
|
|
6
40
|
tags: ["autodocs"],
|
|
7
41
|
render: createRender(Input),
|
|
8
42
|
argTypes: {
|
|
9
|
-
// ...notifyActionArgType,
|
|
10
43
|
label: {
|
|
11
44
|
control: "text",
|
|
12
45
|
description: "Field label rendered above the input.",
|
|
@@ -19,9 +52,9 @@ export default {
|
|
|
19
52
|
control: "text",
|
|
20
53
|
description: "Native HTML input name attribute.",
|
|
21
54
|
},
|
|
22
|
-
|
|
55
|
+
inputType: {
|
|
23
56
|
control: "select",
|
|
24
|
-
options:
|
|
57
|
+
options: nativeInputTypes,
|
|
25
58
|
description: "Native HTML input type.",
|
|
26
59
|
},
|
|
27
60
|
inputmode: {
|
|
@@ -101,6 +134,28 @@ Default.args = {
|
|
|
101
134
|
isFullWidth: false,
|
|
102
135
|
}
|
|
103
136
|
|
|
137
|
+
export const ManagedNativeTypes = {
|
|
138
|
+
render: (args) => html`
|
|
139
|
+
<div style="display: grid; gap: 1rem; max-width: 40rem;">
|
|
140
|
+
${nativeInputTypes.map((inputType) =>
|
|
141
|
+
Input.render({
|
|
142
|
+
...args,
|
|
143
|
+
...nativeInputDefaults[inputType],
|
|
144
|
+
name: `${inputType}-field`,
|
|
145
|
+
label: nativeInputDefaults[inputType].label,
|
|
146
|
+
inputType,
|
|
147
|
+
value: nativeInputDefaults[inputType].value,
|
|
148
|
+
}),
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
`,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
ManagedNativeTypes.args = {
|
|
155
|
+
...Default.args,
|
|
156
|
+
label: "Managed native types",
|
|
157
|
+
}
|
|
158
|
+
|
|
104
159
|
export const WithHint = {}
|
|
105
160
|
WithHint.args = {
|
|
106
161
|
...Default.args,
|
|
@@ -179,3 +234,55 @@ Number.args = {
|
|
|
179
234
|
inputmode: "decimal",
|
|
180
235
|
"data-testid": "amount-input",
|
|
181
236
|
}
|
|
237
|
+
|
|
238
|
+
export const Date = {}
|
|
239
|
+
Date.args = {
|
|
240
|
+
...Default.args,
|
|
241
|
+
label: "Date",
|
|
242
|
+
inputType: "date",
|
|
243
|
+
value: "2026-08-10",
|
|
244
|
+
min: "2024-01-01",
|
|
245
|
+
max: "2027-12-31",
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export const Time = {}
|
|
249
|
+
Time.args = {
|
|
250
|
+
...Default.args,
|
|
251
|
+
label: "Time",
|
|
252
|
+
inputType: "time",
|
|
253
|
+
value: "09:30",
|
|
254
|
+
min: "08:00",
|
|
255
|
+
max: "18:00",
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export const DateTimeLocal = {}
|
|
259
|
+
DateTimeLocal.args = {
|
|
260
|
+
...Default.args,
|
|
261
|
+
label: "Date and time",
|
|
262
|
+
inputType: "datetime-local",
|
|
263
|
+
value: "2026-08-10T09:30",
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export const Month = {}
|
|
267
|
+
Month.args = {
|
|
268
|
+
...Default.args,
|
|
269
|
+
label: "Month",
|
|
270
|
+
inputType: "month",
|
|
271
|
+
value: "2026-08",
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export const Week = {}
|
|
275
|
+
Week.args = {
|
|
276
|
+
...Default.args,
|
|
277
|
+
label: "Week",
|
|
278
|
+
inputType: "week",
|
|
279
|
+
value: "2026-W32",
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export const Color = {}
|
|
283
|
+
Color.args = {
|
|
284
|
+
...Default.args,
|
|
285
|
+
label: "Color",
|
|
286
|
+
inputType: "color",
|
|
287
|
+
value: "#7c3aed",
|
|
288
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
.iw-textarea-field {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--iw-space-1);
|
|
5
|
+
font-family: var(--iw-font-family-sans);
|
|
6
|
+
|
|
7
|
+
&.iw-textarea-full-width .iw-textarea {
|
|
8
|
+
width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.iw-textarea-label {
|
|
12
|
+
font-size: var(--iw-font-size-sm);
|
|
13
|
+
font-weight: var(--iw-font-weight-normal);
|
|
14
|
+
color: var(--iw-color-text);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.iw-textarea-required {
|
|
18
|
+
color: var(--iw-color-error);
|
|
19
|
+
margin-left: var(--iw-space-1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.iw-textarea {
|
|
23
|
+
min-height: 3rem;
|
|
24
|
+
width: min(100%, 100%);
|
|
25
|
+
resize: none;
|
|
26
|
+
padding: var(--iw-space-2) var(--iw-space-3);
|
|
27
|
+
font-family: inherit;
|
|
28
|
+
font-size: var(--iw-font-size-base);
|
|
29
|
+
line-height: 1.5;
|
|
30
|
+
color: var(--iw-input-text);
|
|
31
|
+
background-color: var(--iw-input-bg);
|
|
32
|
+
border: 1px solid var(--iw-input-border);
|
|
33
|
+
border-radius: var(--iw-input-radius);
|
|
34
|
+
outline: none;
|
|
35
|
+
transition: all var(--iw-transition-fast);
|
|
36
|
+
|
|
37
|
+
&::placeholder {
|
|
38
|
+
color: var(--iw-input-text-placeholder);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:hover:not(:disabled) {
|
|
42
|
+
background-color: var(--iw-input-bg-hover);
|
|
43
|
+
border-color: var(--iw-input-border-hover);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:focus:not(:disabled) {
|
|
47
|
+
background-color: var(--iw-input-bg-focus);
|
|
48
|
+
border-color: var(--iw-input-border-focus);
|
|
49
|
+
box-shadow: var(--iw-input-shadow-focus);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:disabled {
|
|
53
|
+
opacity: 0.5;
|
|
54
|
+
cursor: not-allowed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&.iw-textarea-auto-size {
|
|
58
|
+
field-sizing: content;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&.iw-textarea-resizable {
|
|
62
|
+
resize: vertical;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&.iw-textarea-error {
|
|
66
|
+
border-color: var(--iw-color-error);
|
|
67
|
+
|
|
68
|
+
&:focus {
|
|
69
|
+
border-color: var(--iw-color-error);
|
|
70
|
+
box-shadow: 0 0 0 1px var(--iw-color-error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&.iw-textarea-sm {
|
|
75
|
+
padding: var(--iw-space-1) var(--iw-space-2);
|
|
76
|
+
font-size: var(--iw-font-size-sm);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&.iw-textarea-lg {
|
|
80
|
+
padding: var(--iw-space-3) var(--iw-space-4);
|
|
81
|
+
font-size: var(--iw-font-size-lg);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.iw-textarea-hint,
|
|
86
|
+
.iw-textarea-error-message {
|
|
87
|
+
font-size: var(--iw-font-size-sm);
|
|
88
|
+
line-height: var(--iw-line-height-normal);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.iw-textarea-hint {
|
|
92
|
+
color: var(--iw-color-text-muted);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.iw-textarea-error-message {
|
|
96
|
+
color: var(--iw-color-error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/controls/textarea').TextareaProps} TextareaProps
|
|
3
|
+
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { html } from "@inglorious/web"
|
|
7
|
+
import { classMap } from "@inglorious/web/directives/class-map"
|
|
8
|
+
import { ref } from "@inglorious/web/directives/ref"
|
|
9
|
+
|
|
10
|
+
import { applyElementProps } from "../../shared/applyElementProps.js"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Renders a labeled textarea field with optional hint and error messaging.
|
|
14
|
+
* Supports disabled/readonly/required states and emits `onChange` on input.
|
|
15
|
+
* @param {TextareaProps} props
|
|
16
|
+
* @returns {TemplateResult}
|
|
17
|
+
*/
|
|
18
|
+
export function render(props) {
|
|
19
|
+
const {
|
|
20
|
+
id,
|
|
21
|
+
name = "",
|
|
22
|
+
value = "",
|
|
23
|
+
placeholder = "",
|
|
24
|
+
label,
|
|
25
|
+
hint,
|
|
26
|
+
error,
|
|
27
|
+
size = "md",
|
|
28
|
+
rows = 4,
|
|
29
|
+
isDisabled = false,
|
|
30
|
+
isReadOnly = false,
|
|
31
|
+
isRequired = false,
|
|
32
|
+
isFullWidth = false,
|
|
33
|
+
isAutoSize = false,
|
|
34
|
+
isResizable = false,
|
|
35
|
+
onChange,
|
|
36
|
+
onBlur,
|
|
37
|
+
onFocus,
|
|
38
|
+
...rest
|
|
39
|
+
} = props
|
|
40
|
+
|
|
41
|
+
const textareaId = id || name
|
|
42
|
+
|
|
43
|
+
const wrapperClasses = {
|
|
44
|
+
"iw-textarea-field": true,
|
|
45
|
+
"iw-textarea-full-width": isFullWidth,
|
|
46
|
+
"iw-textarea-disabled": isDisabled,
|
|
47
|
+
"iw-textarea-has-error": !!error,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const textareaClasses = {
|
|
51
|
+
"iw-textarea": true,
|
|
52
|
+
[`iw-textarea-${size}`]: size !== "md",
|
|
53
|
+
"iw-textarea-error": !!error,
|
|
54
|
+
"iw-textarea-auto-size": isAutoSize,
|
|
55
|
+
"iw-textarea-resizable": isResizable,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return html`
|
|
59
|
+
<div class=${classMap(wrapperClasses)}>
|
|
60
|
+
${label
|
|
61
|
+
? html`
|
|
62
|
+
<label for=${textareaId} class="iw-textarea-label">
|
|
63
|
+
${label}
|
|
64
|
+
${isRequired
|
|
65
|
+
? html`<span class="iw-textarea-required">*</span>`
|
|
66
|
+
: null}
|
|
67
|
+
</label>
|
|
68
|
+
`
|
|
69
|
+
: null}
|
|
70
|
+
|
|
71
|
+
<textarea
|
|
72
|
+
id=${textareaId}
|
|
73
|
+
name=${name}
|
|
74
|
+
rows=${rows}
|
|
75
|
+
.value=${value}
|
|
76
|
+
placeholder=${placeholder}
|
|
77
|
+
?disabled=${isDisabled}
|
|
78
|
+
?readonly=${isReadOnly}
|
|
79
|
+
?required=${isRequired}
|
|
80
|
+
class=${classMap(textareaClasses)}
|
|
81
|
+
@input=${(event) => onChange?.(event.target.value)}
|
|
82
|
+
@blur=${onBlur}
|
|
83
|
+
@focus=${onFocus}
|
|
84
|
+
${ref((element) => applyElementProps(element, rest))}
|
|
85
|
+
></textarea>
|
|
86
|
+
|
|
87
|
+
${error
|
|
88
|
+
? html`<span class="iw-textarea-error-message">${error}</span>`
|
|
89
|
+
: hint
|
|
90
|
+
? html`<span class="iw-textarea-hint">${hint}</span>`
|
|
91
|
+
: null}
|
|
92
|
+
</div>
|
|
93
|
+
`
|
|
94
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { createRender } from "../../stories/notifyStory.js"
|
|
2
|
+
import { Textarea } from "."
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Controls/Textarea",
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
render: createRender(Textarea),
|
|
8
|
+
argTypes: {
|
|
9
|
+
label: {
|
|
10
|
+
control: "text",
|
|
11
|
+
description: "Field label rendered above the textarea.",
|
|
12
|
+
},
|
|
13
|
+
placeholder: {
|
|
14
|
+
control: "text",
|
|
15
|
+
description: "Placeholder text shown when value is empty.",
|
|
16
|
+
},
|
|
17
|
+
name: {
|
|
18
|
+
control: "text",
|
|
19
|
+
description: "Native HTML textarea name attribute.",
|
|
20
|
+
},
|
|
21
|
+
value: {
|
|
22
|
+
control: "text",
|
|
23
|
+
description: "Current textarea value.",
|
|
24
|
+
},
|
|
25
|
+
hint: {
|
|
26
|
+
control: "text",
|
|
27
|
+
description: "Helper text shown below the field when there is no error.",
|
|
28
|
+
},
|
|
29
|
+
error: {
|
|
30
|
+
control: "text",
|
|
31
|
+
description: "Validation message shown below the field.",
|
|
32
|
+
},
|
|
33
|
+
size: {
|
|
34
|
+
control: "select",
|
|
35
|
+
options: ["sm", "md", "lg"],
|
|
36
|
+
description: "Size scale for internal spacing and typography.",
|
|
37
|
+
},
|
|
38
|
+
rows: {
|
|
39
|
+
control: "number",
|
|
40
|
+
description: "Initial number of visible text rows.",
|
|
41
|
+
},
|
|
42
|
+
isDisabled: {
|
|
43
|
+
control: "boolean",
|
|
44
|
+
description: "Disables user input and interaction.",
|
|
45
|
+
},
|
|
46
|
+
isReadOnly: {
|
|
47
|
+
control: "boolean",
|
|
48
|
+
description: "Prevents editing while keeping the control focusable.",
|
|
49
|
+
},
|
|
50
|
+
isRequired: {
|
|
51
|
+
control: "boolean",
|
|
52
|
+
description: "Marks the field as required.",
|
|
53
|
+
},
|
|
54
|
+
isFullWidth: {
|
|
55
|
+
control: "boolean",
|
|
56
|
+
description: "Expands the textarea width to 100% of its container.",
|
|
57
|
+
},
|
|
58
|
+
isAutoSize: {
|
|
59
|
+
control: "boolean",
|
|
60
|
+
description:
|
|
61
|
+
"Applies field-sizing: content so the textarea grows with its content.",
|
|
62
|
+
},
|
|
63
|
+
isResizable: {
|
|
64
|
+
control: "boolean",
|
|
65
|
+
description: "Enables the browser resize handle for vertical resizing.",
|
|
66
|
+
},
|
|
67
|
+
onChange: { action: "onChange" },
|
|
68
|
+
onBlur: { action: "onBlur" },
|
|
69
|
+
onFocus: { action: "onFocus" },
|
|
70
|
+
},
|
|
71
|
+
parameters: {
|
|
72
|
+
docs: {
|
|
73
|
+
description: {
|
|
74
|
+
component:
|
|
75
|
+
"Multi-line text input with labels, hints, errors, and automatic content sizing support.",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const Default = {}
|
|
82
|
+
Default.args = {
|
|
83
|
+
label: "Bio",
|
|
84
|
+
placeholder: "Tell us a little about yourself",
|
|
85
|
+
name: "bio",
|
|
86
|
+
value: "",
|
|
87
|
+
hint: "",
|
|
88
|
+
error: "",
|
|
89
|
+
size: "md",
|
|
90
|
+
rows: 4,
|
|
91
|
+
isDisabled: false,
|
|
92
|
+
isReadOnly: false,
|
|
93
|
+
isRequired: false,
|
|
94
|
+
isFullWidth: false,
|
|
95
|
+
isAutoSize: false,
|
|
96
|
+
isResizable: false,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const WithHint = {}
|
|
100
|
+
WithHint.args = {
|
|
101
|
+
...Default.args,
|
|
102
|
+
label: "Project notes",
|
|
103
|
+
hint: "Include any implementation details or blockers.",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const WithError = {}
|
|
107
|
+
WithError.args = {
|
|
108
|
+
...Default.args,
|
|
109
|
+
label: "Description",
|
|
110
|
+
value: "This is too long",
|
|
111
|
+
error: "Please keep the description under 280 characters.",
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const Required = {}
|
|
115
|
+
Required.args = {
|
|
116
|
+
...Default.args,
|
|
117
|
+
label: "Message",
|
|
118
|
+
placeholder: "Write a quick message",
|
|
119
|
+
isRequired: true,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const Disabled = {}
|
|
123
|
+
Disabled.args = {
|
|
124
|
+
...Default.args,
|
|
125
|
+
label: "Disabled textarea",
|
|
126
|
+
isDisabled: true,
|
|
127
|
+
value: "Cannot edit this value",
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const Readonly = {}
|
|
131
|
+
Readonly.args = {
|
|
132
|
+
...Default.args,
|
|
133
|
+
label: "Read-only textarea",
|
|
134
|
+
isReadOnly: true,
|
|
135
|
+
value: "This field is read-only and not editable.",
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const Small = {}
|
|
139
|
+
Small.args = {
|
|
140
|
+
...Default.args,
|
|
141
|
+
label: "Small textarea",
|
|
142
|
+
size: "sm",
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const Large = {}
|
|
146
|
+
Large.args = {
|
|
147
|
+
...Default.args,
|
|
148
|
+
label: "Large textarea",
|
|
149
|
+
size: "lg",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const FullWidth = {}
|
|
153
|
+
FullWidth.args = {
|
|
154
|
+
...Default.args,
|
|
155
|
+
label: "Full width textarea",
|
|
156
|
+
placeholder: "This textarea takes the full width of its container",
|
|
157
|
+
isFullWidth: true,
|
|
158
|
+
}
|
|
@@ -147,6 +147,12 @@ function getDefaultItems() {
|
|
|
147
147
|
href: "/controls/icon-button",
|
|
148
148
|
},
|
|
149
149
|
{ id: "input", primary: "Input", href: "/controls/input" },
|
|
150
|
+
{
|
|
151
|
+
id: "file-upload",
|
|
152
|
+
primary: "File Upload",
|
|
153
|
+
href: "/controls/file-upload",
|
|
154
|
+
},
|
|
155
|
+
{ id: "textarea", primary: "Textarea", href: "/controls/textarea" },
|
|
150
156
|
{
|
|
151
157
|
id: "radio-group",
|
|
152
158
|
primary: "Radio Group",
|
|
@@ -6,6 +6,7 @@ import { ButtonGroup } from "../../controls/button-group/index.js"
|
|
|
6
6
|
import { Checkbox } from "../../controls/checkbox/index.js"
|
|
7
7
|
import { Combobox } from "../../controls/combobox/index.js"
|
|
8
8
|
import { Fab } from "../../controls/fab/index.js"
|
|
9
|
+
import { FileUpload } from "../../controls/file-upload/index.js"
|
|
9
10
|
import { IconButton } from "../../controls/icon-button/index.js"
|
|
10
11
|
import { Input } from "../../controls/input/index.js"
|
|
11
12
|
import { RadioGroup } from "../../controls/radio-group/index.js"
|
|
@@ -13,6 +14,7 @@ import { Rating } from "../../controls/rating/index.js"
|
|
|
13
14
|
import { Select } from "../../controls/select/index.js"
|
|
14
15
|
import { Slider } from "../../controls/slider/index.js"
|
|
15
16
|
import { Switch } from "../../controls/switch/index.js"
|
|
17
|
+
import { Textarea } from "../../controls/textarea/index.js"
|
|
16
18
|
import { Avatar } from "../../data-display/avatar/index.js"
|
|
17
19
|
import { Badge } from "../../data-display/badge/index.js"
|
|
18
20
|
import { BeforeAfter } from "../../data-display/before-after/index.js"
|
|
@@ -514,6 +516,73 @@ Input.render({
|
|
|
514
516
|
]),
|
|
515
517
|
},
|
|
516
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
|
+
},
|
|
517
586
|
"/controls/radio-group": {
|
|
518
587
|
summary: "Single-choice selection among visible options.",
|
|
519
588
|
description:
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,8 @@ 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"
|
|
14
16
|
export { BeforeAfter } from "./data-display/before-after/index.js"
|
|
@@ -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
|
|
@@ -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
|
package/types/index.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export type { RatingProps, RatingType } from "./controls/rating"
|
|
|
18
18
|
export type { SelectProps, SelectType, SelectOption } from "./controls/select"
|
|
19
19
|
export type { SliderProps, SliderType } from "./controls/slider"
|
|
20
20
|
export type { SwitchProps, SwitchType } from "./controls/switch"
|
|
21
|
+
export type { TextareaProps, TextareaType } from "./controls/textarea"
|
|
22
|
+
export type { FileUploadProps, FileUploadType } from "./controls/file-upload"
|
|
21
23
|
export type { ContainerProps, ContainerType } from "./layout/container"
|
|
22
24
|
export type { FlexProps, FlexType } from "./layout/flex"
|
|
23
25
|
export type { GridProps, GridType } from "./layout/grid"
|