semantic-forms 5.1.1 → 5.1.2

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.
@@ -25,6 +25,7 @@
25
25
  <ul>
26
26
  <li><a href="/index.html">About</a></li>
27
27
  <li><a href="/usage.html">Usage</a></li>
28
+ <li><a href="/fullDemo.html">Full demo</a></li>
28
29
  </ul>
29
30
  </nav>
30
31
  {pageContent|s}
@@ -534,7 +534,7 @@ form.semanticForms {
534
534
 
535
535
  <h3 id="more">More</h3>
536
536
 
537
- <p>See a page demoing all features <a href="https://rooseveltframework.github.io/semantic-forms/test/semanticForms.html">here</a>.</p>
537
+ <p>See a page demoing all features <a href="https://rooseveltframework.org/docs/semantic-forms/latest/fullDemo/">here</a>.</p>
538
538
  </article>
539
539
  </arg>
540
540
  </include>
package/package.json CHANGED
@@ -8,11 +8,11 @@
8
8
  "url": "https://github.com/rooseveltframework/semantic-forms/graphs/contributors"
9
9
  }
10
10
  ],
11
- "version": "5.1.1",
11
+ "version": "5.1.2",
12
12
  "files": [
13
13
  "dist",
14
14
  "semanticForms.scss",
15
- "docs/statics/pages",
15
+ "docs/statics",
16
16
  "*.md"
17
17
  ],
18
18
  "homepage": "https://rooseveltframework.org/docs/semantic-forms/latest",
@@ -21,25 +21,25 @@
21
21
  "readmeFilename": "README.md",
22
22
  "devDependencies": {
23
23
  "@jsdevtools/coverage-istanbul-loader": "3.0.5",
24
- "@playwright/test": "1.54.1",
25
- "eslint": "9.31.0",
24
+ "@playwright/test": "1.54.2",
25
+ "eslint": "9.32.0",
26
26
  "eslint-plugin-html": "8.1.3",
27
27
  "express": "5.1.0",
28
28
  "nodemon": "3.1.10",
29
29
  "nyc": "17.1.0",
30
- "sass": "1.89.2",
31
- "serve": "14.2.4",
30
+ "sass": "1.90.0",
31
+ "http-server": "14.1.1",
32
32
  "standard": "17.1.2",
33
33
  "webpack-cli": "6.0.1"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "webpack && sass semanticForms.scss dist/semantic-forms.css",
37
37
  "watch": "webpack && sass --watch semanticForms.scss dist/semantic-forms.css",
38
- "d": "nodemon --exec \"npm run build && serve\"",
39
- "dev": "nodemon --exec \"npm run build && serve\"",
40
- "development": "nodemon --exec \"npm run build && serve\"",
41
- "lint": "standard && standard --plugin html test/*.html",
42
- "lint-fix": "standard --fix && standard --plugin html test/*.html --fix",
38
+ "d": "nodemon dev-server.js --ext js,scss",
39
+ "dev": "nodemon dev-server.js --ext js,scss",
40
+ "development": "nodemon dev-server.js --ext js,scss",
41
+ "lint": "standard && standard --plugin html docs/statics/pages/*.html",
42
+ "lint-fix": "standard --fix && standard --plugin html docs/statics/pages/*.html --fix",
43
43
  "test": "npm run build && playwright test",
44
44
  "coverage": "webpack --env coverage && sass semanticForms.scss dist/semantic-forms.css && nyc playwright test && npm run build"
45
45
  },
package/semanticForms.js CHANGED
@@ -68,6 +68,14 @@ const semanticForms = () => {
68
68
  insertAfter(newLabel, input)
69
69
  }
70
70
 
71
+ if (type === 'radio' && input.parentNode.nodeName === 'DD') {
72
+ newLabel.setAttribute('for', input.id)
73
+ input.parentNode.classList.add('singleRadio')
74
+ newLabel.className = ''
75
+ label.setAttribute('hidden', 'hidden')
76
+ insertAfter(newLabel, input)
77
+ }
78
+
71
79
  newLabel.innerHTML = label.innerHTML
72
80
 
73
81
  if (label.hasAttribute('title') && label.getAttribute('data-show-help-icon') !== null && !label.querySelector('span.help')) {
@@ -1,5 +1,5 @@
1
1
  // #region global styles
2
- form.semanticForms, form.semanticForms.light {
2
+ form.semanticForms, form.semanticForms.light, table.semanticForms {
3
3
  --semanticFormsFontFamily: sans-serif;
4
4
  --semanticFormsFormBgColor: #fff;
5
5
  --semanticFormsFormSubBgColor: #f5f5f5;
@@ -22,8 +22,14 @@ form.semanticForms, form.semanticForms.light {
22
22
  --semanticFormsNestedFieldsetBorder: 1px rgba(0, 0, 0, .2) solid;
23
23
  --semanticFormsNestedInputBgColor: #fff;
24
24
  --semanticFormsBorder: 1px var(--semanticFormsBorderColor) solid;
25
+ --semanticFormsTableTitleColor: #fcfcfc;
26
+ --semanticFormsTableHeaderColor: #eeeeee;
27
+ --semanticFormsTableOddColor: #dddddd;
28
+ --semanticFormsTableEvenColor: #cccccc;
29
+ --semanticFormsTableBorder: 1px #c0c0c0 solid;
25
30
  --semanticFormsSelectIcon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23000'%3E%3Cpolygon points='0,0 100,0 50,50'/%3E%3C/svg%3E");
26
- --semanticFormsCustomResizer: url("data:image/svg+xml;utf8,%3Csvg width='20' height='20' viewBox='0 0 5.2916665 5.2916666' version='1.1' id='svg5' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs2' /%3E%3Cg id='layer1'%3E%3Cpath style='fill:%23aaa;stroke:%23aaa;stroke-width:0.264583;stroke-dasharray:none;stroke-opacity:1' d='M 2.8245112,5.0994569 C 4.9396993,2.9842685 4.9396993,2.9842685 4.9396993,2.9842685' id='path1034' /%3E%3Cpath style='fill:%23aaa;stroke:%23aaa;stroke-width:0.264583;stroke-dasharray:none;stroke-opacity:1' d='M 1.515747,5.0406977 C 4.8873304,1.6691138 4.8873304,1.6691138 4.8873304,1.6691138' id='path1034-1' /%3E%3Cpath style='fill:%23aaa;stroke:%23aaa;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1' d='M 0.20657789,5.0909782 C 4.9312207,0.3663348 4.9312207,0.3663348 4.9312207,0.3663348' id='path1034-9' /%3E%3C/g%3E%3C/svg%3E")
31
+ --semanticFormsCustomResizer: url("data:image/svg+xml;utf8,%3Csvg width='20' height='20' viewBox='0 0 5.2916665 5.2916666' version='1.1' id='svg5' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs2' /%3E%3Cg id='layer1'%3E%3Cpath style='fill:%23aaa;stroke:%23aaa;stroke-width:0.264583;stroke-dasharray:none;stroke-opacity:1' d='M 2.8245112,5.0994569 C 4.9396993,2.9842685 4.9396993,2.9842685 4.9396993,2.9842685' id='path1034' /%3E%3Cpath style='fill:%23aaa;stroke:%23aaa;stroke-width:0.264583;stroke-dasharray:none;stroke-opacity:1' d='M 1.515747,5.0406977 C 4.8873304,1.6691138 4.8873304,1.6691138 4.8873304,1.6691138' id='path1034-1' /%3E%3Cpath style='fill:%23aaa;stroke:%23aaa;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1' d='M 0.20657789,5.0909782 C 4.9312207,0.3663348 4.9312207,0.3663348 4.9312207,0.3663348' id='path1034-9' /%3E%3C/g%3E%3C/svg%3E");
32
+ --semanticFormsSearchIcon: url("data:image/svg+xml,%3Csvg%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M15.7955%2015.8111L21%2021M18%2010.5C18%2014.6421%2014.6421%2018%2010.5%2018C6.35786%2018%203%2014.6421%203%2010.5C3%206.35786%206.35786%203%2010.5%203C14.6421%203%2018%206.35786%2018%2010.5Z%22%20stroke%3D%22%23000000%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
27
33
  }
28
34
 
29
35
  @mixin dark-mode {
@@ -42,8 +48,14 @@ form.semanticForms, form.semanticForms.light {
42
48
  --semanticFormsNestedFieldsetBorder: 1px rgba(255, 255, 255, .1) solid;
43
49
  --semanticFormsNestedInputBgColor: rgba(255, 255, 255, .15);
44
50
  --semanticFormsBorder: none;
51
+ --semanticFormsTableTitleColor: #3f3f3f;
52
+ --semanticFormsTableHeaderColor: #4f4f4f;
53
+ --semanticFormsTableOddColor: #3f3f3f;
54
+ --semanticFormsTableEvenColor: #4f4f4f;
55
+ --semanticFormsTableBorder: 1px #656565 solid;
45
56
  --semanticFormsSelectIcon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23fff'%3E%3Cpolygon stroke='%23fff' points='0,0 100,0 50,50'/%3E%3C/svg%3E");
46
57
  --semanticFormsCustomResizer: url("data:image/svg+xml;utf8,%3Csvg width='20' height='20' viewBox='0 0 5.2916665 5.2916666' version='1.1' id='svg5' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs2' /%3E%3Cg id='layer1'%3E%3Cpath style='fill:%23c9c9c9;stroke:%23c9c9c9;stroke-width:0.264583;stroke-dasharray:none;stroke-opacity:1' d='M 2.8245112,5.0994569 C 4.9396993,2.9842685 4.9396993,2.9842685 4.9396993,2.9842685' id='path1034' /%3E%3Cpath style='fill:%23c9c9c9;stroke:%23c9c9c9;stroke-width:0.264583;stroke-dasharray:none;stroke-opacity:1' d='M 1.515747,5.0406977 C 4.8873304,1.6691138 4.8873304,1.6691138 4.8873304,1.6691138' id='path1034-1' /%3E%3Cpath style='fill:%23c9c9c9;stroke:%23c9c9c9;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1' d='M 0.20657789,5.0909782 C 4.9312207,0.3663348 4.9312207,0.3663348 4.9312207,0.3663348' id='path1034-9' /%3E%3C/g%3E%3C/svg%3E");
58
+ --semanticFormsSearchIcon: url("data:image/svg+xml,%3Csvg%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M15.7955%2015.8111L21%2021M18%2010.5C18%2014.6421%2014.6421%2018%2010.5%2018C6.35786%2018%203%2014.6421%203%2010.5C3%206.35786%206.35786%203%2010.5%203C14.6421%203%2018%206.35786%2018%2010.5Z%22%20stroke%3D%22%23FFFFFF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
47
59
 
48
60
  input::-webkit-calendar-picker-indicator {
49
61
  filter: invert(100%);
@@ -53,7 +65,7 @@ form.semanticForms, form.semanticForms.light {
53
65
  @media (prefers-color-scheme: dark) {
54
66
  html:not(.light) {
55
67
  body:not(.light) {
56
- form.semanticForms {
68
+ form.semanticForms, table.semanticForms {
57
69
  @include dark-mode;
58
70
  }
59
71
  }
@@ -62,21 +74,27 @@ form.semanticForms, form.semanticForms.light {
62
74
 
63
75
  html:not(.light) {
64
76
  body:not(.light) {
65
- form.semanticForms.dark {
77
+ form.semanticForms.dark, table.semanticForms.dark {
66
78
  @include dark-mode;
67
79
  }
68
80
  }
69
81
  }
70
82
  // #endregion
71
83
 
72
- form.semanticForms, form.semanticForms.light {
84
+ form.semanticForms,
85
+ form.semanticForms.light,
86
+ table.semanticForms,
87
+ table.semanticForms.light {
73
88
  font-family: var(--semanticFormsFontFamily);
74
89
  width: 100%;
75
90
  container-type: inline-size;
76
91
  container-name: semanticForm;
77
- display: flex;
78
- flex-direction: column;
79
- gap: 10px;
92
+
93
+ &:not(table) {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 10px;
97
+ }
80
98
 
81
99
  // style reset
82
100
  fieldset,
@@ -175,14 +193,35 @@ form.semanticForms, form.semanticForms.light {
175
193
 
176
194
  select {
177
195
  cursor: pointer;
178
- // custom dropdown graphic on select element
179
- background-image: var(--semanticFormsSelectIcon);
180
- background-size: 14px;
181
- background-position: calc(100% - 8px) 16px;
182
- background-repeat: no-repeat;
183
- -webkit-appearance: none;
184
- -moz-appearance: none;
185
- appearance: none;
196
+
197
+ &:not([multiple]) {
198
+ // custom dropdown graphic on select element
199
+ background-image: var(--semanticFormsSelectIcon);
200
+ background-size: 14px;
201
+ background-position: calc(100% - 8px) 16px;
202
+ background-repeat: no-repeat;
203
+ -webkit-appearance: none;
204
+ -moz-appearance: none;
205
+ appearance: none;
206
+ }
207
+
208
+ &[multiple] {
209
+ min-height: 38px;
210
+ height: 100px;
211
+ padding: 0 !important;
212
+
213
+ option {
214
+ padding: 6px 20px;
215
+
216
+ &:first-of-type {
217
+ border-top-left-radius: var(--semanticFormsBorderRadius);
218
+ }
219
+
220
+ &:last-of-type {
221
+ border-bottom-left-radius: var(--semanticFormsBorderRadius);
222
+ }
223
+ }
224
+ }
186
225
  }
187
226
 
188
227
  textarea {
@@ -192,9 +231,27 @@ form.semanticForms, form.semanticForms.light {
192
231
  height: 100px;
193
232
  }
194
233
 
195
- // hide native cancel button
196
- input[type="search"]::-webkit-search-cancel-button {
197
- display: none;
234
+ input[type="search"] {
235
+ background: var(--semanticFormsSearchIcon) no-repeat 10px;
236
+ background-color: var(--semanticFormsFormBgColor);
237
+ background-size: 18px;
238
+ padding-left: 32px !important; // shift values to the right
239
+
240
+ & + label.floatLabelFormAnimatedLabel {
241
+ left: 32px !important; // override float label
242
+ }
243
+
244
+ &:focus,
245
+ &:not(:placeholder-shown) {
246
+ & + label.floatLabelFormAnimatedLabel {
247
+ left: 20px !important;
248
+ }
249
+ }
250
+
251
+ // hide native cancel button
252
+ &::-webkit-search-cancel-button {
253
+ display: none;
254
+ }
198
255
  }
199
256
  // #endregion
200
257
 
@@ -352,7 +409,7 @@ form.semanticForms, form.semanticForms.light {
352
409
  @supports (scrollbar-color: auto) {
353
410
  textarea {
354
411
  scrollbar-color: var(--semanticFormsScrollbarColor) transparent;
355
-
412
+
356
413
  &::-webkit-resizer {
357
414
  border: none;
358
415
  background-image: var(--semanticFormsCustomResizer);
@@ -362,6 +419,10 @@ form.semanticForms, form.semanticForms.light {
362
419
  margin: 0 2px 2px 0;
363
420
  }
364
421
  }
422
+
423
+ select[multiple] {
424
+ scrollbar-color: var(--semanticFormsScrollbarColor) transparent;
425
+ }
365
426
  }
366
427
 
367
428
  // #region container layout
@@ -375,23 +436,53 @@ form.semanticForms, form.semanticForms.light {
375
436
  dd {
376
437
  position: relative;
377
438
 
439
+ &.checkboxes, &.radios {
440
+ display: flex;
441
+ flex-direction: column;
442
+ gap: 10px;
443
+
444
+ // checkbox subtext
445
+ & > label {
446
+ padding-left: 20px;
447
+ font-size: small;
448
+ color: var(--semanticFormsTextColor);
449
+ }
450
+ }
451
+
378
452
  &:not(.checkboxes, .radios) {
379
453
  gap: 10px;
380
454
  max-width: 100%;
381
455
 
382
- // places all items within the <dd> horizontally
383
- &:not(.singleCheckbox) {
456
+ // places all items within the <dd> vertically
457
+ &:not(.singleCheckbox, .singleRadio) {
384
458
  display: flex;
385
459
  flex-direction: column;
386
460
  }
387
461
 
388
- &.singleCheckbox {
389
- display: flex;
462
+ &.singleCheckbox,
463
+ &.singleRadio {
464
+ display: grid;
465
+ grid-template-columns: 15px 1fr;
390
466
  align-items: center;
467
+ padding-left: 11px;
391
468
 
392
- & > :not(input[type='checkbox'], label) {
469
+ & > :not(input[type='checkbox'], input[type='radio'], label) {
393
470
  grid-column: 1 / -1;
394
471
  }
472
+
473
+ // single checkbox subtext
474
+ label:nth-of-type(2) {
475
+ grid-column: 1 / -1;
476
+ padding-left: 20px;
477
+ font-size: small;
478
+ color: var(--semanticFormsTextColor);
479
+ }
480
+ }
481
+
482
+ &.singleRadio {
483
+ input {
484
+ margin: 0;
485
+ }
395
486
  }
396
487
  }
397
488
 
@@ -468,7 +559,7 @@ form.semanticForms, form.semanticForms.light {
468
559
  max-width: 100%;
469
560
  max-width: 365px;
470
561
 
471
- dd:not(.singleCheckbox, .checkboxes, .radios) {
562
+ dd:not(.singleCheckbox, .singleRadio, .checkboxes, .radios) {
472
563
  input:not([type='submit'], [type='reset'], [type='image']),
473
564
  select,
474
565
  textarea {
@@ -620,6 +711,7 @@ form.semanticForms, form.semanticForms.light {
620
711
  position: relative;
621
712
 
622
713
  label {
714
+ z-index: 2; // ensure the hoverable icon is interactable
623
715
  user-select: auto;
624
716
  color: var(--semanticFormsTextColor);
625
717
  font-size: var(--semanticFormsInputFontSize);
@@ -633,7 +725,7 @@ form.semanticForms, form.semanticForms.light {
633
725
  text-overflow: ellipsis;
634
726
  min-width: 0;
635
727
  width: calc(100% + 20px);
636
-
728
+
637
729
  // TODO: remove this? // &:has(span.help) {
638
730
  // width: 100%;
639
731
  // padding-right: 20px;
@@ -976,22 +1068,22 @@ form.semanticForms, form.semanticForms.light {
976
1068
  dl {
977
1069
  grid-auto-flow: dense;
978
1070
  }
979
-
1071
+
980
1072
  // 1, 4, 7, ...
981
1073
  & > dt:nth-of-type(2n + 1),
982
1074
  & > dd:nth-of-type(2n + 1) {
983
1075
  grid-column: 1;
984
1076
  }
985
-
1077
+
986
1078
  // 2, 5, 8, ...
987
1079
  & > dt:nth-of-type(2n + 2),
988
1080
  & > dd:nth-of-type(2n + 2) {
989
1081
  grid-column: 2;
990
1082
  }
991
-
1083
+
992
1084
  dt {
993
1085
  min-width: var(--semanticFormsInputMinWidth) !important; /* this matches the max-width of js-enabled inputs */
994
-
1086
+
995
1087
  // add required asterisk
996
1088
  &:has(+ dd input:required) {
997
1089
  label::after {
@@ -1000,26 +1092,26 @@ form.semanticForms, form.semanticForms.light {
1000
1092
  }
1001
1093
  }
1002
1094
  }
1003
-
1004
-
1095
+
1096
+
1005
1097
  dd {
1006
1098
  margin-bottom: 10px;
1007
1099
  min-width: var(--semanticFormsInputMinWidth) !important; /* this matches the max-width of js-enabled inputs */
1008
-
1100
+
1009
1101
  // nudge single checkboxes to match checkbox groups
1010
1102
  &:not(.checkboxes) {
1011
1103
  input[type=checkbox] {
1012
1104
  margin-left: 10px;
1013
1105
  }
1014
1106
  }
1015
-
1107
+
1016
1108
  // remove "x" padding
1017
1109
  input:not([type=submit], [type=reset], [type=image], [type=checkbox], [type=radio], [type=range]),
1018
1110
  select,
1019
1111
  textarea {
1020
1112
  padding-right: 6px;
1021
1113
  }
1022
-
1114
+
1023
1115
  // subtext
1024
1116
  label {
1025
1117
  margin-top: -5px;
@@ -1028,11 +1120,66 @@ form.semanticForms, form.semanticForms.light {
1028
1120
  }
1029
1121
  }
1030
1122
  }
1031
-
1123
+
1032
1124
  .checkboxes ul,
1033
1125
  .radios ul {
1034
- margin-top: calc(var(--semanticFormsInputTopMargin) / 2);
1126
+ margin-top: var(--semanticFormsInputTopMargin);
1035
1127
  }
1036
1128
  }
1037
1129
  // #endregion
1038
1130
  }
1131
+
1132
+ // #region tables
1133
+
1134
+ .semanticForms table,
1135
+ table.semanticForms {
1136
+ table-layout: fixed;
1137
+ width: 100%;
1138
+ border-spacing: 0;
1139
+ border: var(--semanticFormsBorder);
1140
+ border-radius: 10px;
1141
+ margin-bottom: 15px;
1142
+ overflow: hidden;
1143
+
1144
+ tr.title th {
1145
+ background-color: var(--semanticFormsTableTitleColor);
1146
+ }
1147
+
1148
+ th {
1149
+ background-color: var(--semanticFormsTableHeaderColor);
1150
+ font-weight: bold;
1151
+ }
1152
+
1153
+ tr:nth-child(odd) td {
1154
+ background-color: var(--semanticFormsTableOddColor);
1155
+ }
1156
+
1157
+ tr:nth-child(even) td {
1158
+ background-color: var(--semanticFormsTableEvenColor);
1159
+ }
1160
+
1161
+ td, th {
1162
+ border-right: var(--semanticFormsTableBorder);
1163
+ border-bottom: var(--semanticFormsTableBorder);
1164
+ padding: 7px 10px;
1165
+ }
1166
+
1167
+ th:last-of-type, td:last-of-type {
1168
+ border-right: none;
1169
+ }
1170
+
1171
+ tbody {
1172
+ tr:last-of-type {
1173
+ th, td {
1174
+ border-bottom: none;
1175
+ }
1176
+ }
1177
+ }
1178
+
1179
+ // adjust styling of button menus in tables
1180
+ menu:has(li > input, li > label, li > button, li > select, li > textarea) {
1181
+ width: max-content;
1182
+ }
1183
+
1184
+ }
1185
+ // #endregion