semantic-forms 5.3.5 → 5.3.6
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 +20 -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 +497 -177
- 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 +13 -4
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.6",
|
|
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.110.3",
|
|
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
|
|
|
@@ -83,7 +85,8 @@
|
|
|
83
85
|
border-color: transparent transparent transparent var(--semanticFormsTextColor);
|
|
84
86
|
position: absolute;
|
|
85
87
|
top: 0;
|
|
86
|
-
|
|
88
|
+
// far enough from the words to read as a separate mark rather than part of the first letter
|
|
89
|
+
left: -14px;
|
|
87
90
|
transform: rotate(0);
|
|
88
91
|
transform-origin: .2rem 50%;
|
|
89
92
|
transition: 250ms transform ease;
|
|
@@ -135,7 +138,7 @@
|
|
|
135
138
|
|
|
136
139
|
dl {
|
|
137
140
|
display: grid;
|
|
138
|
-
grid-template-columns: repeat(auto-fit, minmax(var(--semanticFormsInputMinWidth),
|
|
141
|
+
grid-template-columns: repeat(auto-fit, minmax(var(--semanticFormsInputMinWidth), var(--semanticFormsInputColumnMaxWidth)));
|
|
139
142
|
gap: 10px;
|
|
140
143
|
margin: 0;
|
|
141
144
|
|
|
@@ -183,6 +186,11 @@
|
|
|
183
186
|
padding-left: 11px;
|
|
184
187
|
gap: 5px 10px;
|
|
185
188
|
|
|
189
|
+
// a switch is wider than a checkbox, so it needs a wider column to sit in
|
|
190
|
+
&:has(input[type=checkbox][switch]) {
|
|
191
|
+
grid-template-columns: 34px 1fr;
|
|
192
|
+
}
|
|
193
|
+
|
|
186
194
|
// anything that is not the radio/checkbox and its label
|
|
187
195
|
& > :not(input[type='checkbox'], input[type='radio'], label) {
|
|
188
196
|
grid-column: 1 / -1;
|
|
@@ -257,7 +265,8 @@
|
|
|
257
265
|
grid-column: 2 / 3;
|
|
258
266
|
width: 100%;
|
|
259
267
|
max-width: 100%;
|
|
260
|
-
|
|
268
|
+
// 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
|
|
269
|
+
padding: calc(10px - var(--semanticFormsFieldTextInkOffset, 0px)) 10px calc(10px + var(--semanticFormsFieldTextInkOffset, 0px));
|
|
261
270
|
height: var(--semanticFormsInputHeight);
|
|
262
271
|
text-overflow: ellipsis;
|
|
263
272
|
white-space: nowrap;
|