semantic-forms 5.3.5 → 5.3.7
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/CHANGELOG.md +25 -0
- package/CONTRIBUTING.md +39 -7
- package/dist/semantic-forms.cjs +119 -52
- package/dist/semantic-forms.cjs.map +1 -1
- package/dist/semantic-forms.css +498 -178
- package/dist/semantic-forms.css.map +1 -1
- package/dist/semantic-forms.js +153 -80
- package/dist/semantic-forms.js.map +1 -1
- package/dist/semantic-forms.min.js +1 -1
- package/dist/semantic-forms.min.js.map +1 -1
- package/dist/semantic-forms.min.mjs +1 -1
- package/dist/semantic-forms.min.mjs.map +1 -1
- package/dist/semantic-forms.mjs +120 -53
- package/dist/semantic-forms.mjs.map +1 -1
- package/docs/statics/css/semantic-forms-styles.scss +14 -1
- package/docs/statics/js/semantic-forms-demo.js +16 -0
- package/docs/statics/js/semantic-forms-main.js +3 -2
- package/docs/statics/pages/buttons.html +98 -0
- package/docs/statics/pages/checkboxes.html +130 -0
- package/docs/statics/pages/fullDemo.html +1078 -821
- package/docs/statics/pages/inputs.html +384 -0
- package/docs/statics/pages/layout.html +521 -0
- package/docs/statics/pages/layouts/main.html +9 -1
- package/docs/statics/pages/theming.html +39 -0
- package/docs/statics/pages/usage.html +17 -799
- package/package.json +27 -13
- package/semanticForms.js +33 -8
- package/semanticForms.scss +15 -5
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"url": "https://github.com/rooseveltframework/semantic-forms/graphs/contributors"
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
|
-
"version": "5.3.
|
|
11
|
+
"version": "5.3.7",
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
14
|
"semanticForms.scss",
|
|
@@ -26,30 +26,34 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://rooseveltframework.org/docs/semantic-forms/latest",
|
|
28
28
|
"license": "CC-BY-4.0",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.0.0"
|
|
31
|
+
},
|
|
29
32
|
"main": "semanticForms.js",
|
|
30
33
|
"readmeFilename": "README.md",
|
|
31
34
|
"devDependencies": {
|
|
32
35
|
"@jsdevtools/coverage-istanbul-loader": "3.0.5",
|
|
33
|
-
"@playwright/test": "1.
|
|
34
|
-
"eslint": "
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"nodemon": "3.1.11",
|
|
38
|
-
"nyc": "17.1.0",
|
|
39
|
-
"sass": "1.97.3",
|
|
40
|
-
"http-server": "14.1.1",
|
|
36
|
+
"@playwright/test": "1.63.0",
|
|
37
|
+
"eslint-plugin-html": "8.2.0",
|
|
38
|
+
"nyc": "18.0.0",
|
|
39
|
+
"sass": "1.104.1",
|
|
41
40
|
"standard": "17.1.2",
|
|
42
|
-
"webpack-
|
|
41
|
+
"terser-webpack-plugin": "5.6.1",
|
|
42
|
+
"webpack": "5.111.0",
|
|
43
|
+
"webpack-cli": "7.2.3"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|
|
46
|
+
"prepare": "npm run build",
|
|
45
47
|
"build": "webpack && sass semanticForms.scss dist/semantic-forms.css",
|
|
46
48
|
"watch": "webpack && sass --watch semanticForms.scss dist/semantic-forms.css",
|
|
47
|
-
"d": "
|
|
48
|
-
"dev": "
|
|
49
|
-
"development": "
|
|
49
|
+
"d": "node --watch-path=semanticForms.js --watch-path=lib --watch-path=semanticForms.scss --watch-path=styles --watch-path=docs/statics dev-server.js",
|
|
50
|
+
"dev": "node --watch-path=semanticForms.js --watch-path=lib --watch-path=semanticForms.scss --watch-path=styles --watch-path=docs/statics dev-server.js",
|
|
51
|
+
"development": "node --watch-path=semanticForms.js --watch-path=lib --watch-path=semanticForms.scss --watch-path=styles --watch-path=docs/statics dev-server.js",
|
|
50
52
|
"lint": "standard && standard --plugin html docs/statics/pages/*.html",
|
|
51
53
|
"lint-fix": "standard --fix && standard --plugin html docs/statics/pages/*.html --fix",
|
|
52
54
|
"test": "npm run build && playwright test",
|
|
55
|
+
"test-visual": "playwright test --config=playwright.visual.config.js",
|
|
56
|
+
"test-visual-docker": "node visual-tests.js",
|
|
53
57
|
"coverage": "webpack --env coverage && sass semanticForms.scss dist/semantic-forms.css && nyc playwright test && npm run build"
|
|
54
58
|
},
|
|
55
59
|
"standard": {
|
|
@@ -58,6 +62,16 @@
|
|
|
58
62
|
"docs/public"
|
|
59
63
|
]
|
|
60
64
|
},
|
|
65
|
+
"nyc": {
|
|
66
|
+
"include": [
|
|
67
|
+
"semanticForms.js",
|
|
68
|
+
"lib/**/*.js"
|
|
69
|
+
],
|
|
70
|
+
"reporter": [
|
|
71
|
+
"text",
|
|
72
|
+
"html"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
61
75
|
"repository": {
|
|
62
76
|
"type": "git",
|
|
63
77
|
"url": "git://github.com/rooseveltframework/semantic-forms.git"
|
package/semanticForms.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
const { createKeyboardShortcut, shortcutListener } = require('./lib/keyboardShortcuts.js')
|
|
2
2
|
const { enhanceInput, handleUndoRedo } = require('./lib/inputEnhancements.js')
|
|
3
|
+
const { enhanceTabs } = require('./lib/tabs.js')
|
|
4
|
+
const { setInkOffset } = require('./lib/helpers.js')
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
// one observer per tree that has been handed to semanticForms, so that a shadow root gets its own without replacing the document's
|
|
7
|
+
const observers = new WeakMap()
|
|
8
|
+
|
|
9
|
+
// progressively enhances every semanticForms form in a tree
|
|
10
|
+
//
|
|
11
|
+
// root: document or shadow root to search; defaults to the main document
|
|
12
|
+
const semanticForms = (root = document) => {
|
|
5
13
|
// do some feature detection so none of the JS executes if the browser is too old
|
|
6
14
|
if (typeof document.getElementsByClassName !== 'function' || typeof document.querySelector !== 'function' || !document.body.classList || !window.MutationObserver) {
|
|
7
15
|
console.warn('semantic-forms was loaded into an unsupported browser and will not execute.')
|
|
@@ -13,12 +21,15 @@ const semanticForms = () => {
|
|
|
13
21
|
document.addEventListener('keydown', (e) => shortcutListener(e, keyboardShortcuts))
|
|
14
22
|
|
|
15
23
|
// progressively enhance form elements that have the semanticForms class
|
|
16
|
-
const forms =
|
|
24
|
+
const forms = root.querySelectorAll('form.semanticForms:not(.semanticFormsActive), table.semanticForms:not(.semanticFormsActive)')
|
|
17
25
|
|
|
18
26
|
for (const form of forms) {
|
|
19
27
|
form.classList.add('semanticFormsActive')
|
|
20
28
|
if (form.classList.contains('lowFlow')) continue
|
|
21
29
|
|
|
30
|
+
// how far this font's letters sit from the middle of their own line box, which the stylesheet subtracts wherever it centers text. it is measured per form rather than per field, so that a form holding nothing but buttons is corrected too
|
|
31
|
+
setInkOffset(form, form)
|
|
32
|
+
|
|
22
33
|
// update each input in the semantic form
|
|
23
34
|
const inputs = Array.from(form.querySelectorAll('input, textarea, select'))
|
|
24
35
|
for (const input of inputs) {
|
|
@@ -30,32 +41,46 @@ const semanticForms = () => {
|
|
|
30
41
|
keyboardShortcuts.push(shortcut)
|
|
31
42
|
}
|
|
32
43
|
}
|
|
44
|
+
|
|
45
|
+
// done after the inputs so that anything sizing itself measures while it is still on screen
|
|
46
|
+
enhanceTabs(form)
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
// prevents multiple listeners
|
|
36
50
|
document.removeEventListener('keydown', handleUndoRedo)
|
|
37
51
|
document.addEventListener('keydown', handleUndoRedo)
|
|
38
52
|
|
|
39
|
-
// monitor changes to
|
|
40
|
-
if (!
|
|
41
|
-
|
|
53
|
+
// monitor changes to this tree and enhance new semanticForms forms that get added to it
|
|
54
|
+
if (!observers.has(root)) {
|
|
55
|
+
const observer = new window.MutationObserver(mutations => {
|
|
42
56
|
let stop = false
|
|
43
57
|
for (const mutation of mutations) {
|
|
44
58
|
for (const node of mutation.addedNodes) {
|
|
45
59
|
if (node.nodeName === 'FORM' || node?.querySelector?.('form')) {
|
|
46
|
-
semanticForms()
|
|
60
|
+
semanticForms(root)
|
|
47
61
|
stop = true
|
|
48
62
|
}
|
|
49
63
|
}
|
|
50
64
|
if (stop) break
|
|
51
65
|
}
|
|
52
66
|
})
|
|
53
|
-
|
|
67
|
+
|
|
68
|
+
// a shadow root is observed directly; the document is observed through its body
|
|
69
|
+
observer.observe(root === document ? document.body : root, { attributes: false, childList: true, characterData: false, subtree: true })
|
|
70
|
+
observers.set(root, observer)
|
|
71
|
+
|
|
72
|
+
// long standing name for the document's observer, kept for anything already using it
|
|
73
|
+
if (root === document) window.semanticFormsObserver = observer
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
semanticForms.reinitialize = form => {
|
|
77
|
+
// with no form given this just rescans for forms that are not active yet
|
|
78
|
+
if (!form) return semanticForms(root)
|
|
79
|
+
|
|
57
80
|
form.classList.remove('semanticFormsActive')
|
|
58
|
-
|
|
81
|
+
|
|
82
|
+
// the form knows which tree it belongs to, so a form in a shadow root reinitializes there
|
|
83
|
+
semanticForms(form.getRootNode())
|
|
59
84
|
}
|
|
60
85
|
}
|
|
61
86
|
|
package/semanticForms.scss
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
@use 'styles/inputs.scss';
|
|
3
3
|
@use 'styles/labels.scss';
|
|
4
4
|
@use 'styles/colspan.scss';
|
|
5
|
+
@use 'styles/tabs.scss';
|
|
5
6
|
@use 'styles/lowFlow.scss';
|
|
6
7
|
|
|
7
8
|
.semanticForms {
|
|
@@ -66,7 +67,8 @@
|
|
|
66
67
|
margin-bottom: 10px;
|
|
67
68
|
line-height: 1;
|
|
68
69
|
position: absolute;
|
|
69
|
-
|
|
70
|
+
// the ink offset comes off here as it does elsewhere, so the words rather than the box holding them line up
|
|
71
|
+
top: calc(10px - var(--semanticFormsTextInkOffset, 0px));
|
|
70
72
|
left: 22px;
|
|
71
73
|
transition: top 250ms;
|
|
72
74
|
|
|
@@ -82,8 +84,10 @@
|
|
|
82
84
|
border-style: solid;
|
|
83
85
|
border-color: transparent transparent transparent var(--semanticFormsTextColor);
|
|
84
86
|
position: absolute;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
// centered on the line of text beside it: the arrow is 0.8rem tall and the line is 1em, so half the difference puts it in the middle, less the measured ink offset because the letters do not sit in the middle of their own line either. it was pinned to the top of the line before
|
|
88
|
+
top: calc((1em - 0.8rem) / 2 + var(--semanticFormsFieldTextInkOffset, 0px));
|
|
89
|
+
// far enough from the words to read as a separate mark rather than part of the first letter
|
|
90
|
+
left: -14px;
|
|
87
91
|
transform: rotate(0);
|
|
88
92
|
transform-origin: .2rem 50%;
|
|
89
93
|
transition: 250ms transform ease;
|
|
@@ -135,7 +139,7 @@
|
|
|
135
139
|
|
|
136
140
|
dl {
|
|
137
141
|
display: grid;
|
|
138
|
-
grid-template-columns: repeat(auto-fit, minmax(var(--semanticFormsInputMinWidth),
|
|
142
|
+
grid-template-columns: repeat(auto-fit, minmax(var(--semanticFormsInputMinWidth), var(--semanticFormsInputColumnMaxWidth)));
|
|
139
143
|
gap: 10px;
|
|
140
144
|
margin: 0;
|
|
141
145
|
|
|
@@ -183,6 +187,11 @@
|
|
|
183
187
|
padding-left: 11px;
|
|
184
188
|
gap: 5px 10px;
|
|
185
189
|
|
|
190
|
+
// a switch is wider than a checkbox, so it needs a wider column to sit in
|
|
191
|
+
&:has(input[type=checkbox][switch]) {
|
|
192
|
+
grid-template-columns: 34px 1fr;
|
|
193
|
+
}
|
|
194
|
+
|
|
186
195
|
// anything that is not the radio/checkbox and its label
|
|
187
196
|
& > :not(input[type='checkbox'], input[type='radio'], label) {
|
|
188
197
|
grid-column: 1 / -1;
|
|
@@ -257,7 +266,8 @@
|
|
|
257
266
|
grid-column: 2 / 3;
|
|
258
267
|
width: 100%;
|
|
259
268
|
max-width: 100%;
|
|
260
|
-
|
|
269
|
+
// reduced from the default so more of the button's text fits. the vertical halves carry the measured ink offset, as they do on every other control, so the words sit in the middle of the button rather than the box holding them sitting there
|
|
270
|
+
padding: calc(10px - var(--semanticFormsFieldTextInkOffset, 0px)) 10px calc(10px + var(--semanticFormsFieldTextInkOffset, 0px));
|
|
261
271
|
height: var(--semanticFormsInputHeight);
|
|
262
272
|
text-overflow: ellipsis;
|
|
263
273
|
white-space: nowrap;
|