@pageboard/html 0.10.8 → 0.10.13
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/elements/fieldsets.js +76 -0
- package/elements/input-date.js +150 -0
- package/elements/{form-input-file.js → input-file.js} +3 -22
- package/elements/{form-input-property.js → input-property.js} +31 -40
- package/elements/{form-inputs.js → inputs.js} +5 -80
- package/lib/object-fit-images.js +1 -1
- package/package.json +1 -1
- package/ui/fieldset-list.js +3 -4
- package/ui/form.js +10 -3
- package/ui/input-date-slot.js +76 -0
- package/ui/input-date.js +69 -0
- package/ui/input-file.css +50 -39
- package/ui/input-file.js +54 -40
- package/ui/pagination.js +6 -6
- package/ui/select.js +21 -9
- package/ui/sticky.js +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
exports.fieldset = {
|
|
2
|
+
title: 'Fieldset',
|
|
3
|
+
icon: '<i class="folder outline icon"></i>',
|
|
4
|
+
menu: 'form',
|
|
5
|
+
group: 'block',
|
|
6
|
+
context: 'form//',
|
|
7
|
+
properties: {
|
|
8
|
+
name: {
|
|
9
|
+
title: 'Show if input named',
|
|
10
|
+
type: 'string',
|
|
11
|
+
format: 'singleline',
|
|
12
|
+
nullable: true,
|
|
13
|
+
$helper: {
|
|
14
|
+
name: 'element-property',
|
|
15
|
+
existing: true
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
value: {
|
|
19
|
+
title: 'matches this value',
|
|
20
|
+
type: 'string',
|
|
21
|
+
format: 'singleline',
|
|
22
|
+
$filter: {
|
|
23
|
+
name: 'element-value',
|
|
24
|
+
using: 'name'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
plain: {
|
|
28
|
+
title: 'Without borders',
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
default: false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
contents: "fieldset_legend block+",
|
|
34
|
+
html: '<fieldset class="[plain|?]" data-name="[name]" data-value="[value]" is="element-fieldset"></fieldset>',
|
|
35
|
+
scripts: ["../ui/fieldset.js"]
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
exports.fieldset_legend = {
|
|
39
|
+
inplace: true,
|
|
40
|
+
contents: "inline*",
|
|
41
|
+
html: '<legend>Title</legend>'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
exports.fieldset_list = {
|
|
45
|
+
title: 'FieldList',
|
|
46
|
+
menu: "form",
|
|
47
|
+
icon: '<i class="icons"><i class="folder outline icon"></i><i class="corner add icon"></i></i>',
|
|
48
|
+
group: "block",
|
|
49
|
+
context: 'form//',
|
|
50
|
+
priority: 0,
|
|
51
|
+
properties: {
|
|
52
|
+
size: {
|
|
53
|
+
title: 'Minimum size',
|
|
54
|
+
type: "integer",
|
|
55
|
+
minimum: 0,
|
|
56
|
+
default: 1
|
|
57
|
+
},
|
|
58
|
+
prefix: {
|
|
59
|
+
title: 'Prefix',
|
|
60
|
+
description: '',
|
|
61
|
+
type: "string",
|
|
62
|
+
format: 'singleline',
|
|
63
|
+
nullable: true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
contents: [{
|
|
67
|
+
id: 'template',
|
|
68
|
+
nodes: 'block+'
|
|
69
|
+
}],
|
|
70
|
+
html: `<element-fieldset-list data-size="[size]" data-prefix="[prefix]">
|
|
71
|
+
<template block-content="template"></template>
|
|
72
|
+
<div class="view"></div>
|
|
73
|
+
</element-fieldset-list>`,
|
|
74
|
+
scripts: ['../ui/fieldset-list.js'],
|
|
75
|
+
stylesheets: ['../ui/fieldset-list.css']
|
|
76
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
exports.input_date_time = {
|
|
2
|
+
title: 'DateTime',
|
|
3
|
+
icon: '<i class="calendar outline icon"></i>',
|
|
4
|
+
menu: "form",
|
|
5
|
+
required: ["name"],
|
|
6
|
+
group: "block",
|
|
7
|
+
context: 'form//',
|
|
8
|
+
properties: {
|
|
9
|
+
name: {
|
|
10
|
+
title: "Name",
|
|
11
|
+
description: "The form object key",
|
|
12
|
+
type: "string",
|
|
13
|
+
format: "singleline"
|
|
14
|
+
},
|
|
15
|
+
value: {
|
|
16
|
+
title: "Default value",
|
|
17
|
+
nullable: true,
|
|
18
|
+
type: "string",
|
|
19
|
+
format: "singleline"
|
|
20
|
+
},
|
|
21
|
+
placeholder: {
|
|
22
|
+
title: "Placeholder",
|
|
23
|
+
nullable: true,
|
|
24
|
+
type: "string",
|
|
25
|
+
format: "singleline"
|
|
26
|
+
},
|
|
27
|
+
required: {
|
|
28
|
+
title: 'Required',
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
default: false
|
|
31
|
+
},
|
|
32
|
+
disabled: {
|
|
33
|
+
title: 'Disabled',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: false
|
|
36
|
+
},
|
|
37
|
+
format: {
|
|
38
|
+
title: 'Format',
|
|
39
|
+
default: "datetime",
|
|
40
|
+
anyOf: [{
|
|
41
|
+
const: "datetime",
|
|
42
|
+
title: "Date-Time"
|
|
43
|
+
}, {
|
|
44
|
+
const: "date",
|
|
45
|
+
title: "Date"
|
|
46
|
+
}, {
|
|
47
|
+
const: "time",
|
|
48
|
+
title: "Time"
|
|
49
|
+
}]
|
|
50
|
+
},
|
|
51
|
+
step: {
|
|
52
|
+
title: 'Step',
|
|
53
|
+
description: 'rounding/increment in seconds',
|
|
54
|
+
type: 'integer',
|
|
55
|
+
nullable: true,
|
|
56
|
+
anyOf: [{
|
|
57
|
+
const: 60 * 5,
|
|
58
|
+
title: '5 minutes'
|
|
59
|
+
}, {
|
|
60
|
+
const: 60 * 15,
|
|
61
|
+
title: '15 minutes'
|
|
62
|
+
}, {
|
|
63
|
+
const: 60 * 30,
|
|
64
|
+
title: '30 minutes'
|
|
65
|
+
}, {
|
|
66
|
+
const: 60 * 60,
|
|
67
|
+
title: '1 hour'
|
|
68
|
+
}, {
|
|
69
|
+
const: 86400,
|
|
70
|
+
title: '1 day'
|
|
71
|
+
}]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
contents: {
|
|
75
|
+
id: 'label',
|
|
76
|
+
nodes: 'inline*'
|
|
77
|
+
},
|
|
78
|
+
html: `<div class="field">
|
|
79
|
+
<label block-content="label">Label</label>
|
|
80
|
+
<input is="element-input-date"
|
|
81
|
+
name="[name]" disabled="[disabled]" placeholder="[placeholder]"
|
|
82
|
+
required="[required]" value="[value]" step="[step|magnet:]"
|
|
83
|
+
type="[format|eq:datetime:datetime-local]"
|
|
84
|
+
/>
|
|
85
|
+
</div>`,
|
|
86
|
+
scripts: [
|
|
87
|
+
'../ui/input-date.js'
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
exports.input_date_slot = {
|
|
92
|
+
title: 'DateSlot',
|
|
93
|
+
icon: '<i class="calendar outline icon"></i>',
|
|
94
|
+
menu: "form",
|
|
95
|
+
required: ["nameStart", "nameEnd"],
|
|
96
|
+
group: "block",
|
|
97
|
+
context: 'form//',
|
|
98
|
+
properties: {
|
|
99
|
+
nameStart: {
|
|
100
|
+
title: "Name for start date",
|
|
101
|
+
description: "The form object key",
|
|
102
|
+
type: "string",
|
|
103
|
+
format: "singleline"
|
|
104
|
+
},
|
|
105
|
+
nameEnd: {
|
|
106
|
+
title: "Name for end date",
|
|
107
|
+
description: "The form object key",
|
|
108
|
+
type: "string",
|
|
109
|
+
format: "singleline"
|
|
110
|
+
},
|
|
111
|
+
valueStart: {
|
|
112
|
+
title: 'Start time',
|
|
113
|
+
nullable: true,
|
|
114
|
+
type: "string",
|
|
115
|
+
format: "singleline"
|
|
116
|
+
},
|
|
117
|
+
valueEnd: {
|
|
118
|
+
title: 'End time',
|
|
119
|
+
nullable: true,
|
|
120
|
+
type: "string",
|
|
121
|
+
format: "singleline"
|
|
122
|
+
},
|
|
123
|
+
required: {
|
|
124
|
+
title: 'Required',
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
default: false
|
|
127
|
+
},
|
|
128
|
+
disabled: {
|
|
129
|
+
title: 'Disabled',
|
|
130
|
+
type: 'boolean',
|
|
131
|
+
default: false
|
|
132
|
+
},
|
|
133
|
+
step: exports.input_date_time.properties.step,
|
|
134
|
+
format: exports.input_date_time.properties.format
|
|
135
|
+
},
|
|
136
|
+
contents: {
|
|
137
|
+
id: 'label',
|
|
138
|
+
nodes: 'inline*'
|
|
139
|
+
},
|
|
140
|
+
html: `<div class="field">
|
|
141
|
+
<label block-content="label">Label</label>
|
|
142
|
+
<element-input-date-slot type="[format|eq:datetime:datetime-local]" step="[step|magnet:]">
|
|
143
|
+
<input is="element-input-date" name="[nameStart]" value="[valueStart]" />
|
|
144
|
+
<input is="element-input-date" name="[nameEnd]" value="[valueEnd]" />
|
|
145
|
+
</element-input-date-slot>
|
|
146
|
+
</div>`,
|
|
147
|
+
scripts: [
|
|
148
|
+
'../ui/input-date-slot.js'
|
|
149
|
+
]
|
|
150
|
+
};
|
|
@@ -28,11 +28,6 @@ exports.input_file = {
|
|
|
28
28
|
type: 'boolean',
|
|
29
29
|
default: false
|
|
30
30
|
},
|
|
31
|
-
now: {
|
|
32
|
-
title: 'Upload on change',
|
|
33
|
-
type: 'boolean',
|
|
34
|
-
default: false
|
|
35
|
-
},
|
|
36
31
|
limits: {
|
|
37
32
|
title: 'Limits',
|
|
38
33
|
type: 'object',
|
|
@@ -51,13 +46,6 @@ exports.input_file = {
|
|
|
51
46
|
type: 'string'
|
|
52
47
|
},
|
|
53
48
|
default: ['*/*']
|
|
54
|
-
},
|
|
55
|
-
files: {
|
|
56
|
-
title: 'Files',
|
|
57
|
-
description: 'Max number of files',
|
|
58
|
-
type: 'integer',
|
|
59
|
-
minimum: 1,
|
|
60
|
-
default: 1
|
|
61
49
|
}
|
|
62
50
|
}
|
|
63
51
|
}
|
|
@@ -68,16 +56,9 @@ exports.input_file = {
|
|
|
68
56
|
},
|
|
69
57
|
html: `<div class="field">
|
|
70
58
|
<label block-content="label">Label</label>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
disabled="[disabled]" multiple="[limits.files|gt:1|battr]" />
|
|
75
|
-
<label for="[$id]" class="ui icon button">
|
|
76
|
-
<i class="upload icon"></i>
|
|
77
|
-
<i class="delete icon"></i>
|
|
78
|
-
</label>
|
|
79
|
-
<div class="mini floating ui basic label"></div>
|
|
80
|
-
</element-input-file>
|
|
59
|
+
<div class="ui basic label"></div>
|
|
60
|
+
<input is="element-input-file" type="file" id="[$id]" required="[required]"
|
|
61
|
+
disabled="[disabled]" accept="[limits.types|join:,]" name="[name]" placeholder="[placeholder]" />
|
|
81
62
|
</div>`,
|
|
82
63
|
stylesheets: [
|
|
83
64
|
'../lib/components/input.css',
|
|
@@ -54,6 +54,7 @@ exports.input_property = {
|
|
|
54
54
|
// /.api/form wraps it into block.data
|
|
55
55
|
list.shift();
|
|
56
56
|
name = list.join('.');
|
|
57
|
+
const id = scope.$id;
|
|
57
58
|
let prop = el;
|
|
58
59
|
let propKey;
|
|
59
60
|
let required = false;
|
|
@@ -63,12 +64,13 @@ exports.input_property = {
|
|
|
63
64
|
required = prop.required && prop.required.indexOf(propKey) >= 0;
|
|
64
65
|
if (cases) {
|
|
65
66
|
prop = cases[propKey];
|
|
67
|
+
name = list.slice(0, i - 1).concat(list.slice(i + 1)).join('.');
|
|
66
68
|
cases = null;
|
|
67
69
|
} else {
|
|
68
70
|
if (prop.select && prop.select.$data == `0/${propKey}`) {
|
|
69
71
|
cases = prop.selectCases;
|
|
70
72
|
}
|
|
71
|
-
prop = (prop.properties || {})[propKey] || null;
|
|
73
|
+
prop = (prop.items && prop.items.properties || prop.properties || {})[propKey] || null;
|
|
72
74
|
}
|
|
73
75
|
if (prop == null) break;
|
|
74
76
|
}
|
|
@@ -77,8 +79,14 @@ exports.input_property = {
|
|
|
77
79
|
}
|
|
78
80
|
node.textContent = "";
|
|
79
81
|
if (prop.nullable) required = false;
|
|
80
|
-
let
|
|
81
|
-
let
|
|
82
|
+
let propType = prop;
|
|
83
|
+
let multiple = d.multiple;
|
|
84
|
+
if (prop.type == "array" && prop.items && Array.isArray(prop.items) == false) {
|
|
85
|
+
propType = prop.items;
|
|
86
|
+
multiple = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let listOf = propType.anyOf || propType.oneOf;
|
|
82
90
|
if (listOf) {
|
|
83
91
|
const listOfNo = listOf.filter((item) => item.type != "null");
|
|
84
92
|
if (listOfNo.length != listOf.length) {
|
|
@@ -87,7 +95,7 @@ exports.input_property = {
|
|
|
87
95
|
if (listOfNo.length == 1 && listOfNo[0].const === undefined) {
|
|
88
96
|
propType = listOfNo[0];
|
|
89
97
|
listOf = null;
|
|
90
|
-
} else if (
|
|
98
|
+
} else if (multiple) {
|
|
91
99
|
listOf = listOfNo;
|
|
92
100
|
}
|
|
93
101
|
} else if (Array.isArray(prop.type)) {
|
|
@@ -106,7 +114,6 @@ exports.input_property = {
|
|
|
106
114
|
listOf = null; // cannot deal with this for now
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
|
-
if (!propType) propType = prop;
|
|
110
117
|
|
|
111
118
|
if (listOf) {
|
|
112
119
|
if (listOf.length <= d.radios) {
|
|
@@ -126,7 +133,8 @@ exports.input_property = {
|
|
|
126
133
|
content = content.lastElementChild;
|
|
127
134
|
for (const item of listOf) {
|
|
128
135
|
content.appendChild(view.render({
|
|
129
|
-
type:
|
|
136
|
+
type: multiple ? 'input_checkbox' : 'input_radio',
|
|
137
|
+
id,
|
|
130
138
|
data: {
|
|
131
139
|
name: name,
|
|
132
140
|
value: item.type == "null" ? null : item.const,
|
|
@@ -151,10 +159,11 @@ exports.input_property = {
|
|
|
151
159
|
}));
|
|
152
160
|
}
|
|
153
161
|
node.appendChild(view.render({
|
|
162
|
+
id,
|
|
154
163
|
type: 'input_select',
|
|
155
164
|
data: {
|
|
156
165
|
name: name,
|
|
157
|
-
multiple:
|
|
166
|
+
multiple: multiple,
|
|
158
167
|
placeholder: prop.description,
|
|
159
168
|
disabled: d.disabled,
|
|
160
169
|
required: required
|
|
@@ -170,15 +179,16 @@ exports.input_property = {
|
|
|
170
179
|
if (propType.minimum != null && propType.maximum != null) {
|
|
171
180
|
if (propType.maximum - propType.minimum <= d.range) {
|
|
172
181
|
return node.appendChild(view.render({
|
|
182
|
+
id,
|
|
173
183
|
type: 'input_range',
|
|
174
184
|
data: {
|
|
175
185
|
name: name,
|
|
176
186
|
min: propType.minimum,
|
|
177
187
|
max: propType.maximum,
|
|
178
|
-
value:
|
|
188
|
+
value: multiple ? `${propType.minimum}⩽${propType.maximum}` : propType.default,
|
|
179
189
|
disabled: d.disabled,
|
|
180
190
|
required: required,
|
|
181
|
-
multiple:
|
|
191
|
+
multiple: multiple,
|
|
182
192
|
step: step
|
|
183
193
|
},
|
|
184
194
|
content: {
|
|
@@ -188,6 +198,7 @@ exports.input_property = {
|
|
|
188
198
|
}
|
|
189
199
|
}
|
|
190
200
|
node.appendChild(view.render({
|
|
201
|
+
id,
|
|
191
202
|
type: 'input_number',
|
|
192
203
|
data: {
|
|
193
204
|
name: name,
|
|
@@ -204,6 +215,7 @@ exports.input_property = {
|
|
|
204
215
|
}));
|
|
205
216
|
} else if (propType.type == "boolean") {
|
|
206
217
|
node.appendChild(view.render({
|
|
218
|
+
id,
|
|
207
219
|
type: 'input_checkbox',
|
|
208
220
|
data: {
|
|
209
221
|
name: name,
|
|
@@ -215,35 +227,13 @@ exports.input_property = {
|
|
|
215
227
|
label: prop.title
|
|
216
228
|
}
|
|
217
229
|
}));
|
|
218
|
-
} else if (propType.type == "string" &&
|
|
219
|
-
let type = "input_date_time";
|
|
220
|
-
if (!scope.$elements[type]) {
|
|
221
|
-
type = 'input_text';
|
|
222
|
-
}
|
|
230
|
+
} else if (propType.type == "string" && ["date", "time", "date-time"].includes(propType.format)) {
|
|
223
231
|
node.appendChild(view.render({
|
|
224
|
-
|
|
232
|
+
id,
|
|
233
|
+
type: 'input_date_time',
|
|
225
234
|
data: {
|
|
226
235
|
name: name,
|
|
227
|
-
|
|
228
|
-
default: propType.default,
|
|
229
|
-
disabled: d.disabled,
|
|
230
|
-
required: required,
|
|
231
|
-
step: propType.step
|
|
232
|
-
},
|
|
233
|
-
content: {
|
|
234
|
-
label: prop.title
|
|
235
|
-
}
|
|
236
|
-
}));
|
|
237
|
-
} else if (propType.type == "string" && propType.format == "time") {
|
|
238
|
-
let type = "input_date_time";
|
|
239
|
-
if (!scope.$elements[type]) {
|
|
240
|
-
type = 'input_text';
|
|
241
|
-
}
|
|
242
|
-
node.appendChild(view.render({
|
|
243
|
-
type: type,
|
|
244
|
-
data: {
|
|
245
|
-
name: name,
|
|
246
|
-
type: propType.format,
|
|
236
|
+
format: propType.format.replace('-', ''),
|
|
247
237
|
default: propType.default,
|
|
248
238
|
disabled: d.disabled,
|
|
249
239
|
required: required,
|
|
@@ -255,7 +245,7 @@ exports.input_property = {
|
|
|
255
245
|
}));
|
|
256
246
|
} else if (propType.$helper && propType.$helper.name == "href") {
|
|
257
247
|
const limits = {
|
|
258
|
-
files:
|
|
248
|
+
files: multiple ? null : 1
|
|
259
249
|
};
|
|
260
250
|
const filter = propType.$helper.filter;
|
|
261
251
|
if (filter && filter.type) {
|
|
@@ -266,6 +256,7 @@ exports.input_property = {
|
|
|
266
256
|
});
|
|
267
257
|
}
|
|
268
258
|
node.appendChild(view.render({
|
|
259
|
+
id,
|
|
269
260
|
type: 'input_file',
|
|
270
261
|
data: {
|
|
271
262
|
name: name,
|
|
@@ -279,15 +270,15 @@ exports.input_property = {
|
|
|
279
270
|
}
|
|
280
271
|
}));
|
|
281
272
|
} else {
|
|
273
|
+
const type = (propType.format || propType.pattern) ? 'text' : 'textarea';
|
|
282
274
|
node.appendChild(view.render({
|
|
275
|
+
id,
|
|
283
276
|
type: 'input_text',
|
|
284
277
|
data: {
|
|
285
|
-
name
|
|
286
|
-
type: propType.format ? 'text' : 'textarea',
|
|
278
|
+
name, type, required,
|
|
287
279
|
disabled: d.disabled,
|
|
288
280
|
default: propType.default,
|
|
289
|
-
placeholder: propType.description
|
|
290
|
-
required: required
|
|
281
|
+
placeholder: propType.description
|
|
291
282
|
},
|
|
292
283
|
content: {
|
|
293
284
|
label: prop.title
|
|
@@ -1,46 +1,3 @@
|
|
|
1
|
-
exports.fieldset = {
|
|
2
|
-
title: 'Fieldset',
|
|
3
|
-
icon: '<i class="folder outline icon"></i>',
|
|
4
|
-
menu: 'form',
|
|
5
|
-
group: 'block',
|
|
6
|
-
context: 'form//',
|
|
7
|
-
properties: {
|
|
8
|
-
name: {
|
|
9
|
-
title: 'Show if input named',
|
|
10
|
-
type: 'string',
|
|
11
|
-
format: 'singleline',
|
|
12
|
-
nullable: true,
|
|
13
|
-
$helper: {
|
|
14
|
-
name: 'element-property',
|
|
15
|
-
existing: true
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
value: {
|
|
19
|
-
title: 'matches this value',
|
|
20
|
-
type: 'string',
|
|
21
|
-
format: 'singleline',
|
|
22
|
-
$filter: {
|
|
23
|
-
name: 'element-value',
|
|
24
|
-
using: 'name'
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
plain: {
|
|
28
|
-
title: 'Without borders',
|
|
29
|
-
type: 'boolean',
|
|
30
|
-
default: false
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
contents: "fieldset_legend block+",
|
|
34
|
-
html: '<fieldset class="[plain|?]" data-name="[name]" data-value="[value]" is="element-fieldset"></fieldset>',
|
|
35
|
-
scripts: ["../ui/fieldset.js"]
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
exports.fieldset_legend = {
|
|
39
|
-
inplace: true,
|
|
40
|
-
contents: "inline*",
|
|
41
|
-
html: '<legend>Title</legend>'
|
|
42
|
-
};
|
|
43
|
-
|
|
44
1
|
exports.input_button = {
|
|
45
2
|
title: 'Button',
|
|
46
3
|
icon: '<i class="hand pointer icon"></i>',
|
|
@@ -359,8 +316,8 @@ exports.input_checkbox = {
|
|
|
359
316
|
<div class="ui [toggle|?] checkbox">
|
|
360
317
|
<input type="checkbox" required="[required]" disabled="[disabled]"
|
|
361
318
|
name="[name]" value="[value]" checked="[checked]"
|
|
362
|
-
id="for-[name][value|or:|pre:-]" />
|
|
363
|
-
<label block-content="label" for="for-[name][value|or:|pre:-]">Label</label>
|
|
319
|
+
id="for-[name][value|or:|pre:-]-[$id|slice:0:6]" />
|
|
320
|
+
<label block-content="label" for="for-[name][value|or:|pre:-]-[$id|slice:0:6]">Label</label>
|
|
364
321
|
</div>
|
|
365
322
|
</div>`,
|
|
366
323
|
stylesheets: [
|
|
@@ -405,8 +362,8 @@ exports.input_radio = {
|
|
|
405
362
|
<div class="ui radio checkbox">
|
|
406
363
|
<input type="radio" disabled="[disabled]"
|
|
407
364
|
name="[name]" value="[value|or:]" checked="[checked]"
|
|
408
|
-
id="for-[name][value|or:|pre:-]" />
|
|
409
|
-
<label block-content="label" for="for-[name][value|or:|pre:-]">Label</label>
|
|
365
|
+
id="for-[name][value|or:|pre:-]-[$id|slice:0:6]" />
|
|
366
|
+
<label block-content="label" for="for-[name][value|or:|pre:-]-[$id|slice:0:6]">Label</label>
|
|
410
367
|
</div>
|
|
411
368
|
</div>`,
|
|
412
369
|
stylesheets: [
|
|
@@ -502,36 +459,4 @@ exports.input_select_option = {
|
|
|
502
459
|
></element-select-option>`
|
|
503
460
|
};
|
|
504
461
|
|
|
505
|
-
|
|
506
|
-
title: 'FieldList',
|
|
507
|
-
menu: "form",
|
|
508
|
-
icon: '<i class="icons"><i class="folder outline icon"></i><i class="corner add icon"></i></i>',
|
|
509
|
-
group: "block",
|
|
510
|
-
context: 'form//',
|
|
511
|
-
priority: 0,
|
|
512
|
-
properties: {
|
|
513
|
-
size: {
|
|
514
|
-
title: 'Minimum size',
|
|
515
|
-
type: "integer",
|
|
516
|
-
minimum: 0,
|
|
517
|
-
default: 1
|
|
518
|
-
},
|
|
519
|
-
prefix: {
|
|
520
|
-
title: 'Prefix',
|
|
521
|
-
description: '',
|
|
522
|
-
type: "string",
|
|
523
|
-
format: 'singleline',
|
|
524
|
-
nullable: true
|
|
525
|
-
}
|
|
526
|
-
},
|
|
527
|
-
contents: [{
|
|
528
|
-
id: 'template',
|
|
529
|
-
nodes: 'block+'
|
|
530
|
-
}],
|
|
531
|
-
html: `<element-fieldset-list data-size="[size]" data-prefix="[prefix]">
|
|
532
|
-
<template block-content="template"></template>
|
|
533
|
-
<div class="view"></div>
|
|
534
|
-
</element-fieldset-list>`,
|
|
535
|
-
scripts: ['../ui/fieldset-list.js'],
|
|
536
|
-
stylesheets: ['../ui/fieldset-list.css']
|
|
537
|
-
};
|
|
462
|
+
|
package/lib/object-fit-images.js
CHANGED
package/package.json
CHANGED
package/ui/fieldset-list.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
class HTMLElementFieldsetList extends VirtualHTMLElement {
|
|
2
|
-
#size
|
|
3
2
|
fill(values, scope) {
|
|
4
3
|
const list = this.listFromValues(Object.assign({}, values));
|
|
5
4
|
this.resize(list.length, scope);
|
|
@@ -8,7 +7,7 @@ class HTMLElementFieldsetList extends VirtualHTMLElement {
|
|
|
8
7
|
patch(state) {
|
|
9
8
|
this.ownTpl.prerender();
|
|
10
9
|
if (this.isContentEditable) return;
|
|
11
|
-
if (!this
|
|
10
|
+
if (!this.size) this.resize(0, state.scope);
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
setup(state) {
|
|
@@ -17,8 +16,8 @@ class HTMLElementFieldsetList extends VirtualHTMLElement {
|
|
|
17
16
|
|
|
18
17
|
resize(size, scope) {
|
|
19
18
|
const len = Math.max(Number(this.dataset.size) || 0, size);
|
|
20
|
-
if (this
|
|
21
|
-
this
|
|
19
|
+
if (this.size == len) return;
|
|
20
|
+
this.size = len;
|
|
22
21
|
|
|
23
22
|
const tpl = this.ownTpl.content.cloneNode(true);
|
|
24
23
|
for (const node of tpl.querySelectorAll('[block-id]')) {
|
package/ui/form.js
CHANGED
|
@@ -14,7 +14,7 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
14
14
|
if (name && name == this.name) {
|
|
15
15
|
state.vars.submit = true;
|
|
16
16
|
}
|
|
17
|
-
const vars = state.templatesQuery(this);
|
|
17
|
+
const vars = state.templatesQuery(this) || {};
|
|
18
18
|
for (const [key, val] of Object.entries(vars)) {
|
|
19
19
|
this.setAttribute('data-' + key, val);
|
|
20
20
|
}
|
|
@@ -80,6 +80,9 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
80
80
|
if (defVal == "") defVal = null;
|
|
81
81
|
if (!withDefaults && query[node.name] == defVal) {
|
|
82
82
|
query[node.name] = undefined;
|
|
83
|
+
} else {
|
|
84
|
+
// not yet using form-associated custom input
|
|
85
|
+
query[node.name] = node.value;
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
if (query[node.name] === undefined && withDefaults) {
|
|
@@ -181,6 +184,9 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
181
184
|
if (!this.action) return;
|
|
182
185
|
window.sessionStorage.removeItem(this.action);
|
|
183
186
|
}
|
|
187
|
+
handleReset(e, state) {
|
|
188
|
+
this.reset();
|
|
189
|
+
}
|
|
184
190
|
handleSubmit(e, state) {
|
|
185
191
|
if (e.type == "submit") e.preventDefault();
|
|
186
192
|
if (this.isContentEditable) return;
|
|
@@ -234,9 +240,9 @@ class HTMLCustomFormElement extends HTMLFormElement {
|
|
|
234
240
|
|
|
235
241
|
const data = { $query };
|
|
236
242
|
return Promise.all(Array.from(form.elements).filter((node) => {
|
|
237
|
-
return node.
|
|
243
|
+
return Boolean(node.presubmit);
|
|
238
244
|
}).map((input) => {
|
|
239
|
-
return input.
|
|
245
|
+
return input.presubmit();
|
|
240
246
|
})).then(() => {
|
|
241
247
|
data.$query = state.query;
|
|
242
248
|
data.$request = form.read(true);
|
|
@@ -438,6 +444,7 @@ Page.ready((state) => {
|
|
|
438
444
|
if (action == "toggle") {
|
|
439
445
|
action = val ? "enable" : "disable";
|
|
440
446
|
}
|
|
447
|
+
// NB: call Class methods to deal with uninstantiated custom form
|
|
441
448
|
if (action == "enable") {
|
|
442
449
|
HTMLCustomFormElement.prototype.enable.call(form);
|
|
443
450
|
} else if (action == "disable") {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
class HTMLElementInputDateSlot extends VirtualHTMLElement {
|
|
2
|
+
handleChange(e, state) {
|
|
3
|
+
this.update(e.target);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
get type() {
|
|
7
|
+
const type = this.getAttribute('type');
|
|
8
|
+
const step = this.step;
|
|
9
|
+
if (step) {
|
|
10
|
+
if (step >= 86400) return "date";
|
|
11
|
+
else if (type == "date") return "datetime-local";
|
|
12
|
+
}
|
|
13
|
+
return type;
|
|
14
|
+
}
|
|
15
|
+
set type(f) {
|
|
16
|
+
this.setAttribute('type', f);
|
|
17
|
+
}
|
|
18
|
+
get step() {
|
|
19
|
+
const step = parseInt(this.getAttribute('step'));
|
|
20
|
+
if (Number.isNaN(step)) return null;
|
|
21
|
+
else return step;
|
|
22
|
+
}
|
|
23
|
+
set step(val) {
|
|
24
|
+
if (!val) this.removeAttribute('step');
|
|
25
|
+
else this.setAttribute('step', val);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
update(input) {
|
|
29
|
+
const [startEl, endEl] = this.#inputs();
|
|
30
|
+
const isStart = input == startEl;
|
|
31
|
+
|
|
32
|
+
let start = startEl.valueAsDate;
|
|
33
|
+
let end = endEl.valueAsDate;
|
|
34
|
+
if (!start && !end) return;
|
|
35
|
+
if (!start) start = new Date(end);
|
|
36
|
+
else if (!end) end = new Date(start);
|
|
37
|
+
let startPart, endPart;
|
|
38
|
+
let equal = true;
|
|
39
|
+
for (const Part of ['FullYear', 'Month', 'Date', 'Hours', 'Minutes', 'Seconds']) {
|
|
40
|
+
startPart = start[`get${Part}`]();
|
|
41
|
+
endPart = end[`get${Part}`]();
|
|
42
|
+
if (startPart > endPart && equal) {
|
|
43
|
+
if (isStart) {
|
|
44
|
+
end[`set${Part}`](startPart);
|
|
45
|
+
} else {
|
|
46
|
+
start[`set${Part}`](endPart);
|
|
47
|
+
}
|
|
48
|
+
} else if (startPart != endPart) {
|
|
49
|
+
equal = false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
endEl.valueAsDate = end;
|
|
53
|
+
startEl.valueAsDate = start;
|
|
54
|
+
}
|
|
55
|
+
#inputs() {
|
|
56
|
+
return Array.from(this.querySelectorAll('input'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
patch(state) {
|
|
60
|
+
const [ start, end ] = this.#inputs();
|
|
61
|
+
const type = this.type;
|
|
62
|
+
let step = this.step;
|
|
63
|
+
if (step) {
|
|
64
|
+
if (type == "date") step = Math.round(step / 86400);
|
|
65
|
+
start.setAttribute('step', step);
|
|
66
|
+
end.setAttribute('step', step);
|
|
67
|
+
} else {
|
|
68
|
+
start.removeAttribute('step');
|
|
69
|
+
end.removeAttribute('step');
|
|
70
|
+
}
|
|
71
|
+
start.type = end.type = type;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
VirtualHTMLElement.define('element-input-date-slot', HTMLElementInputDateSlot);
|
|
76
|
+
|
package/ui/input-date.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
class HTMLElementInputDate extends HTMLInputElement {
|
|
2
|
+
constructor() {
|
|
3
|
+
super();
|
|
4
|
+
if (this.init) this.init();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
setAttribute(name, value) {
|
|
8
|
+
if (name == "value") {
|
|
9
|
+
this.value = value;
|
|
10
|
+
value = super.value;
|
|
11
|
+
}
|
|
12
|
+
super.setAttribute(name, value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get valueAsDate() {
|
|
16
|
+
let str = super.value;
|
|
17
|
+
if (!str) return null;
|
|
18
|
+
if (this.type == "time") {
|
|
19
|
+
str = `1970-01-01T${str}`;
|
|
20
|
+
}
|
|
21
|
+
const d = new Date(str + 'Z');
|
|
22
|
+
const t = d.getTime();
|
|
23
|
+
if (Number.isNaN(t)) return null;
|
|
24
|
+
const tz = d.getTimezoneOffset();
|
|
25
|
+
d.setTime(t + tz * 60 * 1000);
|
|
26
|
+
return d;
|
|
27
|
+
}
|
|
28
|
+
set valueAsDate(d) {
|
|
29
|
+
let t = d.getTime();
|
|
30
|
+
if (!d || Number.isNaN(t)) {
|
|
31
|
+
super.value = "";
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
d = new Date(t);
|
|
35
|
+
const step = this.step * 1000;
|
|
36
|
+
if (step) {
|
|
37
|
+
t = Math.round(t / step) * step;
|
|
38
|
+
d.setTime(t);
|
|
39
|
+
}
|
|
40
|
+
const tz = d.getTimezoneOffset();
|
|
41
|
+
d.setTime(t - tz * 60 * 1000);
|
|
42
|
+
const str = d.toISOString().replace(/Z$/, '');
|
|
43
|
+
if (this.type == "time") {
|
|
44
|
+
super.value = str.split('T')[1];
|
|
45
|
+
} else if (this.type == "date") {
|
|
46
|
+
super.value = str.split('T')[0];
|
|
47
|
+
} else {
|
|
48
|
+
super.value = str;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get value() {
|
|
53
|
+
return this.valueAsDate?.toISOString();
|
|
54
|
+
}
|
|
55
|
+
set value(str) {
|
|
56
|
+
this.valueAsDate = new Date(str);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get type() {
|
|
60
|
+
return super.type;
|
|
61
|
+
}
|
|
62
|
+
set type(t) {
|
|
63
|
+
const str = super.value;
|
|
64
|
+
super.type = t;
|
|
65
|
+
this.value = str;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
VirtualHTMLElement.define('element-input-date', HTMLElementInputDate, 'input');
|
package/ui/input-file.css
CHANGED
|
@@ -1,47 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
position:absolute !important;
|
|
3
|
-
left:0 !important;
|
|
4
|
-
top:0 !important;
|
|
5
|
-
width:100% !important;
|
|
6
|
-
height:100% !important;
|
|
7
|
-
opacity:0 !important;
|
|
8
|
-
padding:0 !important;
|
|
9
|
-
margin:0 !important;
|
|
10
|
-
}
|
|
11
|
-
element-input-file {
|
|
12
|
-
display:block;
|
|
1
|
+
[block-type="input_file"] {
|
|
13
2
|
position:relative;
|
|
14
3
|
}
|
|
15
|
-
|
|
16
|
-
z-index: 1;
|
|
17
|
-
}
|
|
18
|
-
element-input-file i.icon.upload::before {
|
|
19
|
-
content:'⬆';
|
|
20
|
-
}
|
|
21
|
-
element-input-file > input[type="file"] + .ui.icon.button {
|
|
4
|
+
[block-type="input_file"] > .ui.label {
|
|
22
5
|
display:none;
|
|
6
|
+
position: absolute;
|
|
7
|
+
right:-0.25em;
|
|
8
|
+
top:0.25em;
|
|
23
9
|
}
|
|
24
|
-
|
|
10
|
+
.loading[block-type="input_file"] > .ui.label {
|
|
25
11
|
display:block;
|
|
26
12
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
13
|
+
.form .field > input[type="file"],
|
|
14
|
+
.form .field > input[type="file"]:focus,
|
|
15
|
+
.form .field > input[type="file"]:hover {
|
|
16
|
+
position:relative;
|
|
17
|
+
color: rgba(0 0 0 / 0%);
|
|
18
|
+
}
|
|
19
|
+
.form .field > input[type="file"][filename]::after {
|
|
20
|
+
content:attr(filename);
|
|
21
|
+
color: black;
|
|
22
|
+
text-align: left;
|
|
23
|
+
position: absolute;
|
|
24
|
+
left: 0;
|
|
25
|
+
right: 0;
|
|
26
|
+
padding-left: inherit;
|
|
27
|
+
padding-right: inherit;
|
|
28
|
+
}
|
|
29
|
+
.form .field.error > input[type="file"][filename]::after {
|
|
30
|
+
color:inherit;
|
|
31
|
+
}
|
|
32
|
+
.form .field > input[type="file"]::after,
|
|
33
|
+
.form .field > input[type="file"]:not([filename])::after {
|
|
34
|
+
content:attr(placeholder);
|
|
35
|
+
color: rgba(0 0 0 / 50%);
|
|
36
|
+
float: left;
|
|
37
|
+
}
|
|
38
|
+
input[type="file"]::file-selector-button {
|
|
46
39
|
display:block;
|
|
40
|
+
position:absolute;
|
|
41
|
+
top:0;
|
|
42
|
+
right:0;
|
|
43
|
+
margin:0;
|
|
44
|
+
border-width: 0;
|
|
45
|
+
font-size: 0;
|
|
46
|
+
width:2rem;
|
|
47
|
+
height:100%;
|
|
48
|
+
background-image: url('data:image/svg+xml,<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M28 1.6C29.1.5 30.6-.1 32.2 0c1.5-.095 3 .48 4.1 1.6l26 26a5.6 5.6 0 1 1-7.8 7.9l-17-17v40a5.37 5.37 0 0 1-5.4 5.5h-.048c-3.1-.048-5.5-2.5-5.5-5.5v-40l-17 17a5.6 5.6 0 1 1-7.8-7.9z"/></svg>');
|
|
49
|
+
background-size: 1rem;
|
|
50
|
+
background-repeat: no-repeat;
|
|
51
|
+
background-position: center;
|
|
52
|
+
background-color:inherit;
|
|
53
|
+
z-index: 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
input[type="file"][filename]::file-selector-button {
|
|
57
|
+
background-image: url('data:image/svg+xml,<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg"><path d="m40.9 31.9 21-21a6.4 6.4 0 1 0-9-9l-21 21-21-21a6.4 6.4 0 1 0-9 9l21 21-21 21a6.4 6.4 0 0 0 0 9C3 63 4.6 63.8 6.2 63.8s3.3-.7 4.5-2l21-21 21 21c1.2 1.3 2.8 2 4.5 2s3.2-.7 4.5-2a6.4 6.4 0 0 0 0-9z"/></svg>');
|
|
47
58
|
}
|
package/ui/input-file.js
CHANGED
|
@@ -1,73 +1,88 @@
|
|
|
1
|
-
class HTMLElementInputFile extends
|
|
2
|
-
#xhr
|
|
3
|
-
#promise
|
|
1
|
+
class HTMLElementInputFile extends HTMLInputElement {
|
|
2
|
+
#xhr;
|
|
3
|
+
#promise;
|
|
4
|
+
#value;
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
if (this.init) this.init();
|
|
9
|
+
this.save();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get value() {
|
|
13
|
+
return this.getAttribute('value');
|
|
14
|
+
}
|
|
15
|
+
save() {
|
|
16
|
+
this.#value = this.value;
|
|
17
|
+
}
|
|
18
|
+
reset() {
|
|
19
|
+
if (this.#value != null) this.setAttribute('value', this.#value);
|
|
20
|
+
else this.removeAttribute('value');
|
|
21
|
+
this.value = this.#value;
|
|
22
|
+
}
|
|
23
|
+
set value(str) {
|
|
24
|
+
if (str != null) {
|
|
25
|
+
this.setAttribute('filename', str.split(/\/|\\/).pop());
|
|
26
|
+
} else {
|
|
27
|
+
this.removeAttribute('filename');
|
|
28
|
+
super.value = "";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
4
31
|
captureClick(e, state) {
|
|
5
|
-
|
|
6
|
-
if (!input) return;
|
|
7
|
-
if (input.value) {
|
|
32
|
+
if (super.value) {
|
|
8
33
|
e.preventDefault();
|
|
9
34
|
if (this.#xhr) {
|
|
10
35
|
this.#xhr.abort();
|
|
11
36
|
this.#xhr = null;
|
|
12
37
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
file.reset();
|
|
16
|
-
this.closest('.field').classList.remove('filled', 'loading', 'error', 'success');
|
|
38
|
+
this.value = null;
|
|
39
|
+
this.closest('.field').classList.remove('loading', 'error', 'success');
|
|
17
40
|
} else {
|
|
18
41
|
// ok
|
|
19
42
|
}
|
|
20
43
|
}
|
|
21
44
|
|
|
22
45
|
handleChange(e, state) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
if (super.value) {
|
|
47
|
+
this.value = super.value;
|
|
48
|
+
} else {
|
|
49
|
+
this.value = null;
|
|
27
50
|
}
|
|
28
|
-
if (this.dataset.now != null) this.upload();
|
|
29
51
|
}
|
|
30
52
|
|
|
31
|
-
|
|
53
|
+
presubmit() {
|
|
32
54
|
if (this.#promise) return this.#promise;
|
|
33
|
-
|
|
34
|
-
const input = this.querySelector('input[type="text"]');
|
|
35
|
-
if (!input || !file) throw new Error("Unitialized input-file");
|
|
36
|
-
if (!file.files.length) return Promise.resolve();
|
|
37
|
-
|
|
55
|
+
if (!this.files.length) return Promise.resolve();
|
|
38
56
|
const field = this.closest('.field');
|
|
39
57
|
field.classList.remove('success', 'error');
|
|
40
|
-
const label =
|
|
58
|
+
const label = field.querySelector('.label');
|
|
41
59
|
function track(num) {
|
|
42
60
|
label.innerText = num;
|
|
43
61
|
}
|
|
44
62
|
track(0);
|
|
45
63
|
field.classList.add('loading');
|
|
46
64
|
const p = new Promise((resolve, reject) => {
|
|
47
|
-
const
|
|
48
|
-
function fail(err) {
|
|
65
|
+
const fail = (err) => {
|
|
49
66
|
field.classList.add('error');
|
|
50
67
|
field.classList.remove('loading');
|
|
51
|
-
|
|
68
|
+
this.#xhr = null;
|
|
52
69
|
reject(err);
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
70
|
+
this.#promise = null;
|
|
71
|
+
};
|
|
72
|
+
const pass = (obj) => {
|
|
56
73
|
if (!obj.items || obj.items.length == 0) return fail(new Error("File rejected"));
|
|
57
74
|
const val = obj.items[0];
|
|
58
|
-
|
|
75
|
+
this.setAttribute('value', val);
|
|
59
76
|
field.classList.add('success');
|
|
60
77
|
field.classList.remove('loading');
|
|
61
|
-
|
|
78
|
+
this.#xhr = null;
|
|
62
79
|
resolve();
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
if (
|
|
80
|
+
this.#promise = null;
|
|
81
|
+
};
|
|
82
|
+
if (this.files.length == 0) return resolve(); // or reject ?
|
|
66
83
|
|
|
67
84
|
const fd = new FormData();
|
|
68
|
-
|
|
69
|
-
fd.append("files", file.files[i]);
|
|
70
|
-
}
|
|
85
|
+
fd.append("files", this.files[0]);
|
|
71
86
|
|
|
72
87
|
const xhr = new XMLHttpRequest();
|
|
73
88
|
|
|
@@ -96,7 +111,7 @@ class HTMLElementInputFile extends VirtualHTMLElement {
|
|
|
96
111
|
fail(err);
|
|
97
112
|
});
|
|
98
113
|
try {
|
|
99
|
-
xhr.open("POST", `/.api/upload/${
|
|
114
|
+
xhr.open("POST", `/.api/upload/${this.id}`, true);
|
|
100
115
|
xhr.setRequestHeader('Accept', "application/json; q=1.0");
|
|
101
116
|
xhr.send(fd);
|
|
102
117
|
this.#xhr = xhr;
|
|
@@ -109,6 +124,5 @@ class HTMLElementInputFile extends VirtualHTMLElement {
|
|
|
109
124
|
}
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
127
|
+
VirtualHTMLElement.define('element-input-file', HTMLElementInputFile, 'input');
|
|
128
|
+
|
package/ui/pagination.js
CHANGED
package/ui/select.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class HTMLElementSelect extends VirtualHTMLElement {
|
|
2
|
-
#observer
|
|
2
|
+
#observer;
|
|
3
3
|
|
|
4
4
|
static defaults = {
|
|
5
5
|
placeholder: null,
|
|
@@ -100,11 +100,15 @@ class HTMLElementSelect extends VirtualHTMLElement {
|
|
|
100
100
|
}
|
|
101
101
|
#setPlaceholder(str) {
|
|
102
102
|
const text = this.#text;
|
|
103
|
-
|
|
104
|
-
text.classList.add('default');
|
|
103
|
+
if (!str) str = this.options.placeholder;
|
|
105
104
|
|
|
106
105
|
const defaultOption = this.#select.querySelector('option[value=""]');
|
|
107
|
-
if (defaultOption)
|
|
106
|
+
if (defaultOption) {
|
|
107
|
+
if (!str) str = defaultOption.innerHTML;
|
|
108
|
+
else defaultOption.innerHTML = str;
|
|
109
|
+
}
|
|
110
|
+
text.textContent = str;
|
|
111
|
+
text.classList.add('default');
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
#menuOption(val) {
|
|
@@ -124,11 +128,13 @@ class HTMLElementSelect extends VirtualHTMLElement {
|
|
|
124
128
|
const select = this.#select;
|
|
125
129
|
if (!select) return;
|
|
126
130
|
const menu = this.#menu;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
menu.children.forEach(item => {
|
|
132
|
+
const val = item.dataset.value;
|
|
133
|
+
select.insertAdjacentHTML(
|
|
134
|
+
'beforeEnd',
|
|
135
|
+
`<option value="${val == null ? '' : val}">${item.innerHTML}</option>`
|
|
136
|
+
);
|
|
137
|
+
});
|
|
132
138
|
}
|
|
133
139
|
setup(state) {
|
|
134
140
|
this.#observer = new MutationObserver((mutations) => this.#fillSelect());
|
|
@@ -154,6 +160,12 @@ class HTMLElementSelect extends VirtualHTMLElement {
|
|
|
154
160
|
for (const node of this.querySelectorAll('.ui.label')) node.remove();
|
|
155
161
|
}
|
|
156
162
|
select.name = this.options.name;
|
|
163
|
+
if (!select.required) {
|
|
164
|
+
const menu = this.#menu;
|
|
165
|
+
if (!menu.querySelector('element-select-option[data-value=""]')) {
|
|
166
|
+
menu.insertAdjacentHTML('afterBegin', `<element-select-option data-value="" block-type="input_select_option" class="item">-</element-select-option>`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
157
169
|
this.#fillSelect();
|
|
158
170
|
}
|
|
159
171
|
|