@pageboard/html 0.10.10 → 0.10.12
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/form.js +1 -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} +7 -32
- package/elements/{form-inputs.js → inputs.js} +1 -76
- 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 +3 -3
- 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
|
+
};
|
package/elements/form.js
CHANGED
|
@@ -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',
|
|
@@ -70,7 +70,7 @@ exports.input_property = {
|
|
|
70
70
|
if (prop.select && prop.select.$data == `0/${propKey}`) {
|
|
71
71
|
cases = prop.selectCases;
|
|
72
72
|
}
|
|
73
|
-
prop = (prop.properties || {})[propKey] || null;
|
|
73
|
+
prop = (prop.items && prop.items.properties || prop.properties || {})[propKey] || null;
|
|
74
74
|
}
|
|
75
75
|
if (prop == null) break;
|
|
76
76
|
}
|
|
@@ -227,37 +227,13 @@ exports.input_property = {
|
|
|
227
227
|
label: prop.title
|
|
228
228
|
}
|
|
229
229
|
}));
|
|
230
|
-
} else if (propType.type == "string" &&
|
|
231
|
-
let type = "input_date_time";
|
|
232
|
-
if (!scope.$elements[type]) {
|
|
233
|
-
type = 'input_text';
|
|
234
|
-
}
|
|
235
|
-
node.appendChild(view.render({
|
|
236
|
-
id,
|
|
237
|
-
type: type,
|
|
238
|
-
data: {
|
|
239
|
-
name: name,
|
|
240
|
-
type: propType.format,
|
|
241
|
-
default: propType.default,
|
|
242
|
-
disabled: d.disabled,
|
|
243
|
-
required: required,
|
|
244
|
-
step: propType.step
|
|
245
|
-
},
|
|
246
|
-
content: {
|
|
247
|
-
label: prop.title
|
|
248
|
-
}
|
|
249
|
-
}));
|
|
250
|
-
} else if (propType.type == "string" && propType.format == "time") {
|
|
251
|
-
let type = "input_date_time";
|
|
252
|
-
if (!scope.$elements[type]) {
|
|
253
|
-
type = 'input_text';
|
|
254
|
-
}
|
|
230
|
+
} else if (propType.type == "string" && ["date", "time", "date-time"].includes(propType.format)) {
|
|
255
231
|
node.appendChild(view.render({
|
|
256
232
|
id,
|
|
257
|
-
type:
|
|
233
|
+
type: 'input_date_time',
|
|
258
234
|
data: {
|
|
259
235
|
name: name,
|
|
260
|
-
|
|
236
|
+
format: propType.format.replace('-', ''),
|
|
261
237
|
default: propType.default,
|
|
262
238
|
disabled: d.disabled,
|
|
263
239
|
required: required,
|
|
@@ -294,16 +270,15 @@ exports.input_property = {
|
|
|
294
270
|
}
|
|
295
271
|
}));
|
|
296
272
|
} else {
|
|
273
|
+
const type = (propType.format || propType.pattern) ? 'text' : 'textarea';
|
|
297
274
|
node.appendChild(view.render({
|
|
298
275
|
id,
|
|
299
276
|
type: 'input_text',
|
|
300
277
|
data: {
|
|
301
|
-
name
|
|
302
|
-
type: (propType.pattern || propType.format) ? 'text' : 'textarea',
|
|
278
|
+
name, type, required,
|
|
303
279
|
disabled: d.disabled,
|
|
304
280
|
default: propType.default,
|
|
305
|
-
placeholder: propType.description
|
|
306
|
-
required: required
|
|
281
|
+
placeholder: propType.description
|
|
307
282
|
},
|
|
308
283
|
content: {
|
|
309
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>',
|
|
@@ -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,
|
|
@@ -129,10 +129,10 @@ class HTMLElementSelect extends VirtualHTMLElement {
|
|
|
129
129
|
if (!select) return;
|
|
130
130
|
const menu = this.#menu;
|
|
131
131
|
menu.children.forEach(item => {
|
|
132
|
-
const val = item.dataset.value
|
|
132
|
+
const val = item.dataset.value;
|
|
133
133
|
select.insertAdjacentHTML(
|
|
134
134
|
'beforeEnd',
|
|
135
|
-
`<option value="${val}">${item.innerHTML}</option>`
|
|
135
|
+
`<option value="${val == null ? '' : val}">${item.innerHTML}</option>`
|
|
136
136
|
);
|
|
137
137
|
});
|
|
138
138
|
}
|