@pageboard/html 0.10.9 → 0.10.14

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/ui/input-file.css CHANGED
@@ -1,47 +1,58 @@
1
- element-input-file > input[type="text"] + input[type="file"] {
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
- element-input-file > .ui.icon.button {
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
- element-input-file > input[type="text"] + input[type="file"] + .ui.icon.button {
10
+ .loading[block-type="input_file"] > .ui.label {
25
11
  display:block;
26
12
  }
27
- element-input-file .button > .upload {
28
- pointer-events:none;
29
- }
30
- element-input-file input[type="text"]:not([value]) ~ .button > .delete,
31
- element-input-file input[type="text"][value=""] ~ .button > .delete,
32
- element-input-file input[type="text"][value]:not([value=""]) ~ .button > .upload,
33
- .loading.field element-input-file .button > .upload,
34
- .success.field element-input-file .button > .upload {
35
- display:none;
36
- }
37
- element-input-file input[type="text"][value]:not([value=""]) ~ .button > .delete,
38
- .loading.field element-input-file .button > .delete,
39
- .success.field element-input-file .button > .delete {
40
- display:inline-block;
41
- }
42
- element-input-file > .ui.label {
43
- display:none;
44
- }
45
- .loading.field element-input-file > .ui.label {
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: transparent !important;
18
+ }
19
+ .form .field > input[type="file"][value]::after {
20
+ content:attr(value);
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"][value]::after {
30
+ color:inherit;
31
+ }
32
+ .form .field > input[type="file"]::after,
33
+ .form .field > input[type="file"]:not([value])::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"][value]::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,93 @@
1
- class HTMLElementInputFile extends VirtualHTMLElement {
2
- #xhr
3
- #promise
1
+ class HTMLElementInputFile extends HTMLInputElement {
2
+ #xhr;
3
+ #promise;
4
+ #defaultValue;
5
+
6
+ /* Since input[type=file] does not allow setting "value" property,
7
+ * it is stored in the "value" attribute,
8
+ * forcing defaultValue to be handled with a private field.
9
+ * The "filename" attribute is used to display both selected value property,
10
+ * and filled value attribute.
11
+ */
12
+
13
+ constructor() {
14
+ super();
15
+ if (this.init) this.init();
16
+ this.save();
17
+
18
+ }
19
+ get defaultValue() {
20
+ return this.#defaultValue;
21
+ }
22
+ set defaultValue(str) {
23
+ this.#defaultValue = str;
24
+ }
25
+ get value() {
26
+ return this.getAttribute('value');
27
+ }
28
+ set value(str) {
29
+ if (str != null) {
30
+ this.setAttribute('value', str);
31
+ } else {
32
+ this.removeAttribute('value');
33
+ super.value = "";
34
+ }
35
+ }
4
36
  captureClick(e, state) {
5
- const input = this.querySelector('input[type="text"]');
6
- if (!input) return;
7
- if (input.value) {
37
+ if (this.value) {
8
38
  e.preventDefault();
9
39
  if (this.#xhr) {
10
40
  this.#xhr.abort();
11
41
  this.#xhr = null;
12
42
  }
13
- input.value = '';
14
- const file = this.querySelector('input[type="file"]');
15
- file.reset();
16
- this.closest('.field').classList.remove('filled', 'loading', 'error', 'success');
43
+ this.value = null;
44
+ this.closest('.field').classList.remove('loading', 'error', 'success');
17
45
  } else {
18
46
  // ok
19
47
  }
20
48
  }
21
49
 
22
50
  handleChange(e, state) {
23
- const input = this.querySelector('input[type="text"]');
24
- if (!input) return;
25
- if (e.target.type == "file" && e.target.value) {
26
- input.value = (e.target.value || "").split(/\/|\\/).pop();
51
+ if (super.value) {
52
+ this.value = super.value;
53
+ } else {
54
+ this.value = null;
27
55
  }
28
- if (this.dataset.now != null) this.upload();
29
56
  }
30
57
 
31
- upload() {
58
+ presubmit() {
32
59
  if (this.#promise) return this.#promise;
33
- const file = this.querySelector('input[type="file"]');
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
-
60
+ if (!this.files.length) return Promise.resolve();
38
61
  const field = this.closest('.field');
39
62
  field.classList.remove('success', 'error');
40
- const label = this.querySelector('.label');
63
+ const label = field.querySelector('.label');
41
64
  function track(num) {
42
65
  label.innerText = num;
43
66
  }
44
67
  track(0);
45
68
  field.classList.add('loading');
46
69
  const p = new Promise((resolve, reject) => {
47
- const me = this;
48
- function fail(err) {
70
+ const fail = (err) => {
49
71
  field.classList.add('error');
50
72
  field.classList.remove('loading');
51
- me.#xhr = null;
73
+ this.#xhr = null;
52
74
  reject(err);
53
- me.#promise = null;
54
- }
55
- function pass(obj) {
75
+ this.#promise = null;
76
+ };
77
+ const pass = (obj) => {
56
78
  if (!obj.items || obj.items.length == 0) return fail(new Error("File rejected"));
57
79
  const val = obj.items[0];
58
- input.value = val;
80
+ this.value = val;
59
81
  field.classList.add('success');
60
82
  field.classList.remove('loading');
61
- me.#xhr = null;
83
+ this.#xhr = null;
62
84
  resolve();
63
- me.#promise = null;
64
- }
65
- if (file.files.length == 0) return resolve(); // or reject ?
85
+ this.#promise = null;
86
+ };
87
+ if (this.files.length == 0) return resolve(); // or reject ?
66
88
 
67
89
  const fd = new FormData();
68
- for (let i = 0; i < file.files.length; i++) {
69
- fd.append("files", file.files[i]);
70
- }
90
+ fd.append("files", this.files[0]);
71
91
 
72
92
  const xhr = new XMLHttpRequest();
73
93
 
@@ -96,7 +116,7 @@ class HTMLElementInputFile extends VirtualHTMLElement {
96
116
  fail(err);
97
117
  });
98
118
  try {
99
- xhr.open("POST", `/.api/upload/${file.id}`, true);
119
+ xhr.open("POST", `/.api/upload/${this.id}`, true);
100
120
  xhr.setRequestHeader('Accept', "application/json; q=1.0");
101
121
  xhr.send(fd);
102
122
  this.#xhr = xhr;
@@ -109,6 +129,5 @@ class HTMLElementInputFile extends VirtualHTMLElement {
109
129
  }
110
130
  }
111
131
 
112
- Page.setup(() => {
113
- VirtualHTMLElement.define('element-input-file', HTMLElementInputFile);
114
- });
132
+ VirtualHTMLElement.define('element-input-file', HTMLElementInputFile, 'input');
133
+
package/ui/item.css CHANGED
@@ -23,7 +23,7 @@
23
23
  border-radius: 0rem;
24
24
  box-shadow: none;
25
25
  transition: box-shadow 0.1s ease;
26
- z-index: '';
26
+ z-index: "";
27
27
  font-size: 1em;
28
28
  }
29
29
  .ui.items > .ui a {
@@ -50,7 +50,7 @@
50
50
 
51
51
  .ui.items > .ui::after {
52
52
  display: block;
53
- content: ' ';
53
+ content: " ";
54
54
  height: 0px;
55
55
  clear: both;
56
56
  overflow: hidden;
@@ -74,7 +74,7 @@
74
74
  float: none;
75
75
  margin: 0em;
76
76
  padding: 0em;
77
- max-height: '';
77
+ max-height: "";
78
78
  align-self: top;
79
79
  width:175px;
80
80
  }
@@ -106,7 +106,7 @@
106
106
  }
107
107
  .ui.items > .ui > .content::after {
108
108
  display: block;
109
- content: ' ';
109
+ content: " ";
110
110
  height: 0px;
111
111
  clear: both;
112
112
  overflow: hidden;
@@ -122,15 +122,15 @@
122
122
  }
123
123
  .ui.items > .ui > .content > .header {
124
124
  display: inline-block;
125
- margin: -0.21425em 0em 0em;
126
- font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
125
+ margin: -0.2142em 0em 0em;
126
+ font-family: Lato, "Helvetica Neue", Arial, Helvetica, sans-serif;
127
127
  font-weight: bold;
128
- color: rgba(0, 0, 0, 0.85);
128
+ color: rgb(0 0 0 / 85%);
129
129
  }
130
130
 
131
131
  /* Default Header Size */
132
132
  .ui.items > .ui > .content > .header:not(.ui) {
133
- font-size: 1.28571429em;
133
+ font-size: 1.2857em;
134
134
  }
135
135
 
136
136
  /*--------------
@@ -150,12 +150,12 @@
150
150
 
151
151
  .ui.items > .ui .content img {
152
152
  align-self: middle;
153
- width: '';
153
+ width: "";
154
154
  }
155
155
  .ui.items > .ui img.avatar,
156
156
  .ui.items > .ui .avatar img {
157
- width: '';
158
- height: '';
157
+ width: "";
158
+ height: "";
159
159
  border-radius: 500rem;
160
160
  }
161
161
 
@@ -168,7 +168,7 @@
168
168
  max-width: auto;
169
169
  font-size: 1em;
170
170
  line-height: 1.4285em;
171
- color: rgba(0, 0, 0, 0.87);
171
+ color: rgb(0 0 0 / 87%);
172
172
  }
173
173
 
174
174
  /*--------------
@@ -187,10 +187,10 @@
187
187
  ---------------*/
188
188
 
189
189
  .ui.items > .ui .meta {
190
- margin: 0.5em 0em 0.5em;
190
+ margin: 0.5em 0em;
191
191
  font-size: 1em;
192
192
  line-height: 1em;
193
- color: rgba(0, 0, 0, 0.6);
193
+ color: rgb(0 0 0 / 60%);
194
194
  }
195
195
  .ui.items > .ui .meta * {
196
196
  margin-right: 0.3em;
@@ -210,16 +210,16 @@
210
210
 
211
211
  /* Generic */
212
212
  .ui.items > .ui > .content a:not(.ui) {
213
- color: '';
213
+ color: "";
214
214
  transition: color 0.1s ease;
215
215
  }
216
216
  .ui.items > .ui > .content a:not(.ui):hover {
217
- color: '';
217
+ color: "";
218
218
  }
219
219
 
220
220
  /* Header */
221
221
  .ui.items > .ui > .content > a.header {
222
- color: rgba(0, 0, 0, 0.85);
222
+ color: rgb(0 0 0 / 85%);
223
223
  }
224
224
  .ui.items > .ui > .content > a.header:hover {
225
225
  color: #1e70bf;
@@ -227,10 +227,10 @@
227
227
 
228
228
  /* Meta */
229
229
  .ui.items > .ui .meta > a:not(.ui) {
230
- color: rgba(0, 0, 0, 0.4);
230
+ color: rgb(0 0 0 / 40%);
231
231
  }
232
232
  .ui.items > .ui .meta > a:not(.ui):hover {
233
- color: rgba(0, 0, 0, 0.87);
233
+ color: rgb(0 0 0 / 87%);
234
234
  }
235
235
 
236
236
  /*--------------
@@ -244,11 +244,11 @@
244
244
  /* Icon */
245
245
  .ui.items > .ui > .content .favorite.icon {
246
246
  cursor: pointer;
247
- opacity: 0.75;
247
+ opacity: 75%;
248
248
  transition: color 0.1s ease;
249
249
  }
250
250
  .ui.items > .ui > .content .favorite.icon:hover {
251
- opacity: 1;
251
+ opacity: 100%;
252
252
  color: #FFB70A;
253
253
  }
254
254
  .ui.items > .ui > .content .active.favorite.icon {
@@ -261,11 +261,11 @@
261
261
  /* Icon */
262
262
  .ui.items > .ui > .content .like.icon {
263
263
  cursor: pointer;
264
- opacity: 0.75;
264
+ opacity: 75%;
265
265
  transition: color 0.1s ease;
266
266
  }
267
267
  .ui.items > .ui > .content .like.icon:hover {
268
- opacity: 1;
268
+ opacity: 100%;
269
269
  color: #FF2733;
270
270
  }
271
271
  .ui.items > .ui > .content .active.like.icon {
@@ -282,10 +282,10 @@
282
282
  background: none;
283
283
  margin: 0.5rem 0em 0em;
284
284
  width: 100%;
285
- padding: 0em 0em 0em;
285
+ padding: 0em;
286
286
  top: 0em;
287
287
  left: 0em;
288
- color: rgba(0, 0, 0, 0.4);
288
+ color: rgb(0 0 0 / 40%);
289
289
  box-shadow: none;
290
290
  transition: color 0.1s ease;
291
291
  border-top: none;
@@ -298,7 +298,7 @@
298
298
  }
299
299
  .ui.items > .ui .extra::after {
300
300
  display: block;
301
- content: ' ';
301
+ content: " ";
302
302
  height: 0px;
303
303
  clear: both;
304
304
  overflow: hidden;
@@ -319,7 +319,7 @@
319
319
  margin-right: 1em;
320
320
  }
321
321
  .ui.three.items > .ui > .image {
322
- width: calc(33.33333333% - 1em);
322
+ width: calc(33.3333% - 1em);
323
323
  margin-right: 1em;
324
324
  }
325
325
  .ui.four.items > .ui > .image {
@@ -331,11 +331,11 @@
331
331
  margin-right: 0.75em;
332
332
  }
333
333
  .ui.six.items > .ui > .image {
334
- width: calc(16.66666667% - 0.75em);
334
+ width: calc(16.6667% - 0.75em);
335
335
  margin-right: 0.75em;
336
336
  }
337
337
  .ui.seven.items > .ui > .image {
338
- width: calc(14.28571429% - 0.5em);
338
+ width: calc(14.2857% - 0.5em);
339
339
  margin-right: 0.5em;
340
340
  }
341
341
 
@@ -409,7 +409,7 @@
409
409
  --------------------*/
410
410
 
411
411
  .ui.divided.items > .ui {
412
- border-top: 1px solid rgba(34, 36, 38, 0.15);
412
+ border-top: 1px solid rgb(34 36 38 / 15%);
413
413
  margin: 0em;
414
414
  padding: 1em 0em;
415
415
  }
package/ui/layout.css CHANGED
@@ -53,7 +53,7 @@
53
53
  }
54
54
 
55
55
  .inverted.layout {
56
- background: rgba(0, 0, 0, 0.8);
56
+ background: rgb(0 0 0 / 80%);
57
57
  }
58
58
 
59
59
  .inverted.layout p {
package/ui/loading.css CHANGED
@@ -21,24 +21,24 @@
21
21
  0% {
22
22
  width:0%;
23
23
  margin-left:0px;
24
- opacity:0.3;
24
+ opacity:30%;
25
25
  }
26
26
  50% {
27
27
  width:20px;
28
28
  margin-left:-10px;
29
- opacity:1;
29
+ opacity:100%;
30
30
  }
31
31
  100% {
32
32
  width:0%;
33
33
  margin-left:0px;
34
- opacity:0.3;
34
+ opacity:30%;
35
35
  }
36
36
  }
37
37
 
38
38
  .loading[data-src]::after {
39
39
  display: block;
40
40
  animation: scanner 6s steps(3, end) infinite;
41
- content: '';
41
+ content: "";
42
42
  width: 20px;
43
43
  height: 0;
44
44
  border-top: 4px dotted #aaa;
@@ -48,17 +48,17 @@
48
48
 
49
49
  .error[data-src]::after {
50
50
  display: block;
51
- content: '';
51
+ content: "";
52
52
  }
53
53
 
54
54
  .waiting[data-src]::after {
55
55
  display:block;
56
- content: '?';
56
+ content: "?";
57
57
  }
58
58
 
59
59
  .denied[data-src]::after {
60
60
  display:block;
61
- content: '🛇';
61
+ content: "🛇";
62
62
  }
63
63
 
64
64
  .error[data-src],
package/ui/menu.css CHANGED
@@ -11,7 +11,7 @@
11
11
  width: auto;
12
12
  height: auto;
13
13
  top: 100% !important;
14
- opacity: 1;
14
+ opacity: 100%;
15
15
  }
16
16
 
17
17
  /* menu item with icon */
package/ui/pagination.js CHANGED
@@ -1,10 +1,10 @@
1
1
  class HTMLElementPagination extends HTMLAnchorElement {
2
- #observer
3
- #queue
4
- #reached
5
- #size
6
- #visible
7
- #continue
2
+ #observer;
3
+ #queue;
4
+ #reached;
5
+ #size;
6
+ #visible;
7
+ #continue;
8
8
 
9
9
  constructor() {
10
10
  super();
package/ui/rating.css CHANGED
@@ -24,7 +24,7 @@ element-rating .icon {
24
24
  height: auto;
25
25
  transition: opacity 0.1s ease, background 0.1s ease, text-shadow 0.1s ease, color 0.1s ease;
26
26
  backface-visibility: hidden;
27
- color: rgba(0, 0, 0, 0.15);
27
+ color: rgb(0 0 0 / 15%);
28
28
  }
29
29
  element-rating .icon:last-child {
30
30
  padding-right:0;
@@ -40,20 +40,20 @@ element-rating .icon::before {
40
40
 
41
41
  /* Active Icon */
42
42
  element-rating .active.icon {
43
- color: rgba(0, 0, 0, 0.85);
43
+ color: rgb(0 0 0 / 85%);
44
44
  }
45
45
 
46
46
  /* Selected Icon */
47
47
  element-rating .icon.selected,
48
48
  element-rating .icon.selected.active {
49
- color: rgba(0, 0, 0, 0.87);
49
+ color: rgb(0 0 0 / 87%);
50
50
  }
51
51
 
52
52
  /* Star */
53
53
 
54
54
  /* Inactive */
55
55
  element-rating.star .icon {
56
- color: rgba(0, 0, 0, 0.15);
56
+ color: rgb(0 0 0 / 15%);
57
57
  text-shadow: none;
58
58
  }
59
59
 
@@ -73,7 +73,7 @@ element-rating.star .icon.selected.active {
73
73
  /* Heart */
74
74
 
75
75
  element-rating.heart .icon {
76
- color: rgba(0, 0, 0, 0.15);
76
+ color: rgb(0 0 0 / 15%);
77
77
  text-shadow: none !important;
78
78
  }
79
79
 
@@ -101,10 +101,10 @@ element-rating.disabled .icon {
101
101
 
102
102
  /* Selected Rating */
103
103
  element-rating.selected .active.icon {
104
- opacity: 1;
104
+ opacity: 100%;
105
105
  }
106
106
  element-rating .icon.selected,
107
107
  element-rating.selected .icon.selected {
108
- opacity: 1;
108
+ opacity: 100%;
109
109
  }
110
110
 
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
- text.textContent = str || this.options.placeholder;
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) defaultOption.innerHTML = str || "-";
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
- select.innerHTML = '<option selected value="">-</option>';
128
- menu.children.forEach(item => select.insertAdjacentHTML(
129
- 'beforeEnd',
130
- `<option value="${item.dataset.value || item.innerText.trim()}">${item.innerHTML}</option>`
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