@fieldia/widgets 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +19 -2
- package/package.json +35 -3
- package/src/index.d.mts +7 -0
- package/src/index.d.ts +7 -0
- package/src/index.js +11 -0
- package/src/index.js.map +1 -0
- package/src/index.mjs +8 -0
- package/src/index.mjs.map +1 -0
- package/src/lib/extras.d.mts +5 -0
- package/src/lib/extras.d.ts +5 -0
- package/src/lib/extras.js +130 -0
- package/src/lib/extras.js.map +1 -0
- package/src/lib/extras.mjs +124 -0
- package/src/lib/extras.mjs.map +1 -0
- package/src/lib/files.d.mts +3 -0
- package/src/lib/files.d.ts +3 -0
- package/src/lib/files.js +115 -0
- package/src/lib/files.js.map +1 -0
- package/src/lib/files.mjs +112 -0
- package/src/lib/files.mjs.map +1 -0
- package/src/lib/labels.d.mts +17 -0
- package/src/lib/labels.d.ts +17 -0
- package/src/lib/labels.js +62 -0
- package/src/lib/labels.js.map +1 -0
- package/src/lib/labels.mjs +59 -0
- package/src/lib/labels.mjs.map +1 -0
- package/src/lib/lines.d.mts +2 -0
- package/src/lib/lines.d.ts +2 -0
- package/src/lib/lines.js +140 -0
- package/src/lib/lines.js.map +1 -0
- package/src/lib/lines.mjs +136 -0
- package/src/lib/lines.mjs.map +1 -0
- package/src/lib/relations.d.mts +6 -0
- package/src/lib/relations.d.ts +6 -0
- package/src/lib/relations.js +362 -0
- package/src/lib/relations.js.map +1 -0
- package/src/lib/relations.mjs +355 -0
- package/src/lib/relations.mjs.map +1 -0
- package/src/lib/styles.d.mts +16 -0
- package/src/lib/styles.d.ts +16 -0
- package/src/lib/styles.js +394 -0
- package/src/lib/styles.js.map +1 -0
- package/src/lib/styles.mjs +390 -0
- package/src/lib/styles.mjs.map +1 -0
- package/src/lib/widgets.d.mts +44 -0
- package/src/lib/widgets.d.ts +44 -0
- package/src/lib/widgets.js +356 -0
- package/src/lib/widgets.js.map +1 -0
- package/src/lib/widgets.mjs +351 -0
- package/src/lib/widgets.mjs.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fieldia-dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fieldia/widgets
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The field inputs of [Fieldia](https://fieldia.dev), in plain DOM: text, numbers,
|
|
4
|
+
choices, ratings and scales, dates, many2one search pickers, tags, tables of
|
|
5
|
+
lines, file and image uploads, formatted text and JSON. Two skins, chosen per
|
|
6
|
+
form with `data-fd-skin`:
|
|
7
|
+
|
|
8
|
+
- `underline` — labels beside values, quiet underlined inputs (a business sheet)
|
|
9
|
+
- `outlined` — labels above boxed inputs (Ant Design style)
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @fieldia/widgets
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Most apps never import this package directly: [`@fieldia/viewer`](https://www.npmjs.com/package/@fieldia/viewer)
|
|
16
|
+
lays a page out with these widgets. Import it to write your own widget — a
|
|
17
|
+
`WidgetFactory` returns `{ element, update(state), focus() }` — and register it
|
|
18
|
+
with the viewer's `widgets` option under `type` or `type.widget`.
|
|
19
|
+
|
|
20
|
+
MIT licensed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fieldia/widgets",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fieldia field inputs in plain DOM, with the underline and outlined skins.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://fieldia.dev",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"main": "./src/index.js",
|
|
9
|
+
"module": "./src/index.mjs",
|
|
10
|
+
"types": "./src/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": {
|
|
14
|
+
"import": "./src/index.d.mts",
|
|
15
|
+
"require": "./src/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"import": "./src/index.mjs",
|
|
18
|
+
"require": "./src/index.js",
|
|
19
|
+
"default": "./src/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@fieldia/core": "0.1.0",
|
|
25
|
+
"tslib": "^2.8.1"
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/fieldia-dev/fieldia",
|
|
31
|
+
"directory": "libs/widgets"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"src",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
]
|
|
6
38
|
}
|
package/src/index.d.mts
ADDED
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./lib/widgets"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./lib/styles"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./lib/labels"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./lib/relations"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./lib/lines"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./lib/files"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./lib/extras"), exports);
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/widgets/src/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B;AAC9B,uDAA6B;AAC7B,uDAA6B;AAC7B,0DAAgC;AAChC,sDAA4B;AAC5B,sDAA4B;AAC5B,uDAA6B"}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './lib/widgets.mjs';
|
|
2
|
+
export * from './lib/styles.mjs';
|
|
3
|
+
export * from './lib/labels.mjs';
|
|
4
|
+
export * from './lib/relations.mjs';
|
|
5
|
+
export * from './lib/lines.mjs';
|
|
6
|
+
export * from './lib/files.mjs';
|
|
7
|
+
export * from './lib/extras.mjs';
|
|
8
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../../libs/widgets/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WidgetFactory } from './widgets.mjs';
|
|
2
|
+
/** Keep text structure and http(s)/mailto links; drop every other element and attribute. */
|
|
3
|
+
export declare function sanitizeHtml(html: string, document?: Document): string;
|
|
4
|
+
export declare const htmlWidget: WidgetFactory;
|
|
5
|
+
export declare const jsonWidget: WidgetFactory;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WidgetFactory } from './widgets';
|
|
2
|
+
/** Keep text structure and http(s)/mailto links; drop every other element and attribute. */
|
|
3
|
+
export declare function sanitizeHtml(html: string, document?: Document): string;
|
|
4
|
+
export declare const htmlWidget: WidgetFactory;
|
|
5
|
+
export declare const jsonWidget: WidgetFactory;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jsonWidget = exports.htmlWidget = void 0;
|
|
4
|
+
exports.sanitizeHtml = sanitizeHtml;
|
|
5
|
+
const labels_1 = require("./labels");
|
|
6
|
+
/**
|
|
7
|
+
* Formatted text and JSON. Formatted text is edited in place (the browser's
|
|
8
|
+
* own shortcuts give bold, italic and underline) and every value — typed,
|
|
9
|
+
* pasted or loaded — passes through `sanitizeHtml`, so stored markup can never
|
|
10
|
+
* run script in the page.
|
|
11
|
+
*/
|
|
12
|
+
const ALLOWED = new Set([
|
|
13
|
+
'P', 'BR', 'B', 'STRONG', 'I', 'EM', 'U', 'S', 'UL', 'OL', 'LI', 'A', 'H1', 'H2', 'H3', 'H4',
|
|
14
|
+
'BLOCKQUOTE', 'CODE', 'PRE', 'SPAN', 'DIV',
|
|
15
|
+
]);
|
|
16
|
+
const DROPPED_WITH_CONTENT = new Set(['SCRIPT', 'STYLE', 'IFRAME', 'OBJECT', 'EMBED', 'TEMPLATE', 'NOSCRIPT', 'SVG', 'MATH']);
|
|
17
|
+
/** Keep text structure and http(s)/mailto links; drop every other element and attribute. */
|
|
18
|
+
function sanitizeHtml(html, document = globalThis.document) {
|
|
19
|
+
const box = document.createElement('template');
|
|
20
|
+
box.innerHTML = html;
|
|
21
|
+
const clean = (parent) => {
|
|
22
|
+
for (const node of [...parent.childNodes]) {
|
|
23
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
24
|
+
continue;
|
|
25
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
26
|
+
node.remove();
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const element = node;
|
|
30
|
+
if (DROPPED_WITH_CONTENT.has(element.tagName)) {
|
|
31
|
+
element.remove();
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
clean(element);
|
|
35
|
+
if (!ALLOWED.has(element.tagName)) {
|
|
36
|
+
element.replaceWith(...element.childNodes);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const href = element.tagName === 'A' ? element.getAttribute('href') : null;
|
|
40
|
+
for (const attribute of [...element.attributes])
|
|
41
|
+
element.removeAttribute(attribute.name);
|
|
42
|
+
if (href && /^(https?:|mailto:)/i.test(href.trim()))
|
|
43
|
+
element.setAttribute('href', href.trim());
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
clean(box.content);
|
|
47
|
+
const out = document.createElement('div');
|
|
48
|
+
out.append(box.content);
|
|
49
|
+
return out.innerHTML;
|
|
50
|
+
}
|
|
51
|
+
const htmlWidget = ({ form, name, id, document }) => {
|
|
52
|
+
const area = document.createElement('div');
|
|
53
|
+
area.id = id;
|
|
54
|
+
area.className = 'fd-input fd-richtext';
|
|
55
|
+
area.setAttribute('role', 'textbox');
|
|
56
|
+
area.setAttribute('aria-multiline', 'true');
|
|
57
|
+
area.setAttribute('contenteditable', 'true');
|
|
58
|
+
let last = '';
|
|
59
|
+
area.addEventListener('input', () => {
|
|
60
|
+
const clean = sanitizeHtml(area.innerHTML, document);
|
|
61
|
+
last = clean;
|
|
62
|
+
form.setValue(name, clean.trim() === '' ? null : clean);
|
|
63
|
+
});
|
|
64
|
+
area.addEventListener('blur', () => {
|
|
65
|
+
// Show what was stored: anything unsafe that was typed or pasted is gone.
|
|
66
|
+
if (area.innerHTML !== last)
|
|
67
|
+
area.innerHTML = last;
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
element: area,
|
|
71
|
+
focus: () => area.focus(),
|
|
72
|
+
update(state) {
|
|
73
|
+
const clean = sanitizeHtml(typeof state.value === 'string' ? state.value : '', document);
|
|
74
|
+
if (clean !== last || document.activeElement !== area) {
|
|
75
|
+
if (area.innerHTML !== clean)
|
|
76
|
+
area.innerHTML = clean;
|
|
77
|
+
last = clean;
|
|
78
|
+
}
|
|
79
|
+
area.setAttribute('contenteditable', String(!state.readonly));
|
|
80
|
+
area.setAttribute('aria-readonly', String(state.readonly));
|
|
81
|
+
area.setAttribute('aria-invalid', String(state.invalid));
|
|
82
|
+
if (state.describedBy)
|
|
83
|
+
area.setAttribute('aria-describedby', state.describedBy);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
exports.htmlWidget = htmlWidget;
|
|
88
|
+
const jsonWidget = ({ form, name, id, document, labels = labels_1.WIDGET_LABELS.en }) => {
|
|
89
|
+
const element = document.createElement('div');
|
|
90
|
+
element.className = 'fd-json';
|
|
91
|
+
const area = document.createElement('textarea');
|
|
92
|
+
area.id = id;
|
|
93
|
+
area.className = 'fd-input fd-textarea fd-code';
|
|
94
|
+
area.spellcheck = false;
|
|
95
|
+
const error = document.createElement('div');
|
|
96
|
+
error.className = 'fd-cell-error';
|
|
97
|
+
error.hidden = true;
|
|
98
|
+
element.append(area, error);
|
|
99
|
+
area.addEventListener('input', () => {
|
|
100
|
+
if (area.value.trim() === '') {
|
|
101
|
+
error.hidden = true;
|
|
102
|
+
form.setValue(name, null);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(area.value);
|
|
107
|
+
error.hidden = true;
|
|
108
|
+
form.setValue(name, parsed);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
error.hidden = false;
|
|
112
|
+
error.textContent = labels.invalidJson;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
element,
|
|
117
|
+
focus: () => area.focus(),
|
|
118
|
+
update(state) {
|
|
119
|
+
if (document.activeElement !== area) {
|
|
120
|
+
const text = state.value === null || state.value === undefined ? '' : JSON.stringify(state.value, null, 2);
|
|
121
|
+
if (area.value !== text)
|
|
122
|
+
area.value = text;
|
|
123
|
+
}
|
|
124
|
+
area.readOnly = state.readonly;
|
|
125
|
+
area.setAttribute('aria-invalid', String(state.invalid || !error.hidden));
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
exports.jsonWidget = jsonWidget;
|
|
130
|
+
//# sourceMappingURL=extras.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extras.js","sourceRoot":"","sources":["../../../../../libs/widgets/src/lib/extras.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { WIDGET_LABELS } from './labels.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Formatted text and JSON. Formatted text is edited in place (the browser's
|
|
4
|
+
* own shortcuts give bold, italic and underline) and every value — typed,
|
|
5
|
+
* pasted or loaded — passes through `sanitizeHtml`, so stored markup can never
|
|
6
|
+
* run script in the page.
|
|
7
|
+
*/
|
|
8
|
+
const ALLOWED = new Set([
|
|
9
|
+
'P', 'BR', 'B', 'STRONG', 'I', 'EM', 'U', 'S', 'UL', 'OL', 'LI', 'A', 'H1', 'H2', 'H3', 'H4',
|
|
10
|
+
'BLOCKQUOTE', 'CODE', 'PRE', 'SPAN', 'DIV',
|
|
11
|
+
]);
|
|
12
|
+
const DROPPED_WITH_CONTENT = new Set(['SCRIPT', 'STYLE', 'IFRAME', 'OBJECT', 'EMBED', 'TEMPLATE', 'NOSCRIPT', 'SVG', 'MATH']);
|
|
13
|
+
/** Keep text structure and http(s)/mailto links; drop every other element and attribute. */
|
|
14
|
+
export function sanitizeHtml(html, document = globalThis.document) {
|
|
15
|
+
const box = document.createElement('template');
|
|
16
|
+
box.innerHTML = html;
|
|
17
|
+
const clean = (parent) => {
|
|
18
|
+
for (const node of [...parent.childNodes]) {
|
|
19
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
20
|
+
continue;
|
|
21
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
22
|
+
node.remove();
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const element = node;
|
|
26
|
+
if (DROPPED_WITH_CONTENT.has(element.tagName)) {
|
|
27
|
+
element.remove();
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
clean(element);
|
|
31
|
+
if (!ALLOWED.has(element.tagName)) {
|
|
32
|
+
element.replaceWith(...element.childNodes);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const href = element.tagName === 'A' ? element.getAttribute('href') : null;
|
|
36
|
+
for (const attribute of [...element.attributes])
|
|
37
|
+
element.removeAttribute(attribute.name);
|
|
38
|
+
if (href && /^(https?:|mailto:)/i.test(href.trim()))
|
|
39
|
+
element.setAttribute('href', href.trim());
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
clean(box.content);
|
|
43
|
+
const out = document.createElement('div');
|
|
44
|
+
out.append(box.content);
|
|
45
|
+
return out.innerHTML;
|
|
46
|
+
}
|
|
47
|
+
export const htmlWidget = ({ form, name, id, document }) => {
|
|
48
|
+
const area = document.createElement('div');
|
|
49
|
+
area.id = id;
|
|
50
|
+
area.className = 'fd-input fd-richtext';
|
|
51
|
+
area.setAttribute('role', 'textbox');
|
|
52
|
+
area.setAttribute('aria-multiline', 'true');
|
|
53
|
+
area.setAttribute('contenteditable', 'true');
|
|
54
|
+
let last = '';
|
|
55
|
+
area.addEventListener('input', () => {
|
|
56
|
+
const clean = sanitizeHtml(area.innerHTML, document);
|
|
57
|
+
last = clean;
|
|
58
|
+
form.setValue(name, clean.trim() === '' ? null : clean);
|
|
59
|
+
});
|
|
60
|
+
area.addEventListener('blur', () => {
|
|
61
|
+
// Show what was stored: anything unsafe that was typed or pasted is gone.
|
|
62
|
+
if (area.innerHTML !== last)
|
|
63
|
+
area.innerHTML = last;
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
element: area,
|
|
67
|
+
focus: () => area.focus(),
|
|
68
|
+
update(state) {
|
|
69
|
+
const clean = sanitizeHtml(typeof state.value === 'string' ? state.value : '', document);
|
|
70
|
+
if (clean !== last || document.activeElement !== area) {
|
|
71
|
+
if (area.innerHTML !== clean)
|
|
72
|
+
area.innerHTML = clean;
|
|
73
|
+
last = clean;
|
|
74
|
+
}
|
|
75
|
+
area.setAttribute('contenteditable', String(!state.readonly));
|
|
76
|
+
area.setAttribute('aria-readonly', String(state.readonly));
|
|
77
|
+
area.setAttribute('aria-invalid', String(state.invalid));
|
|
78
|
+
if (state.describedBy)
|
|
79
|
+
area.setAttribute('aria-describedby', state.describedBy);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
export const jsonWidget = ({ form, name, id, document, labels = WIDGET_LABELS.en }) => {
|
|
84
|
+
const element = document.createElement('div');
|
|
85
|
+
element.className = 'fd-json';
|
|
86
|
+
const area = document.createElement('textarea');
|
|
87
|
+
area.id = id;
|
|
88
|
+
area.className = 'fd-input fd-textarea fd-code';
|
|
89
|
+
area.spellcheck = false;
|
|
90
|
+
const error = document.createElement('div');
|
|
91
|
+
error.className = 'fd-cell-error';
|
|
92
|
+
error.hidden = true;
|
|
93
|
+
element.append(area, error);
|
|
94
|
+
area.addEventListener('input', () => {
|
|
95
|
+
if (area.value.trim() === '') {
|
|
96
|
+
error.hidden = true;
|
|
97
|
+
form.setValue(name, null);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(area.value);
|
|
102
|
+
error.hidden = true;
|
|
103
|
+
form.setValue(name, parsed);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
error.hidden = false;
|
|
107
|
+
error.textContent = labels.invalidJson;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
element,
|
|
112
|
+
focus: () => area.focus(),
|
|
113
|
+
update(state) {
|
|
114
|
+
if (document.activeElement !== area) {
|
|
115
|
+
const text = state.value === null || state.value === undefined ? '' : JSON.stringify(state.value, null, 2);
|
|
116
|
+
if (area.value !== text)
|
|
117
|
+
area.value = text;
|
|
118
|
+
}
|
|
119
|
+
area.readOnly = state.readonly;
|
|
120
|
+
area.setAttribute('aria-invalid', String(state.invalid || !error.hidden));
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=extras.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extras.mjs","sourceRoot":"","sources":["../../../../../libs/widgets/src/lib/extras.ts"],"names":[],"mappings":"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"}
|
package/src/lib/files.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.imageWidget = exports.binaryWidget = void 0;
|
|
4
|
+
const core_1 = require("@fieldia/core");
|
|
5
|
+
const labels_1 = require("./labels");
|
|
6
|
+
/**
|
|
7
|
+
* File and image fields. A chosen or dropped file is read into the value as
|
|
8
|
+
* base64, the way a backend's binary fields carry it; a stored one may carry
|
|
9
|
+
* a `url` instead. Size and type limits are checked by the field's validation.
|
|
10
|
+
*/
|
|
11
|
+
function read(file) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const reader = new FileReader();
|
|
14
|
+
reader.onload = () => {
|
|
15
|
+
const url = String(reader.result ?? '');
|
|
16
|
+
resolve({ name: file.name, type: file.type, size: file.size, data: url.slice(url.indexOf(',') + 1) });
|
|
17
|
+
};
|
|
18
|
+
reader.onerror = () => reject(reader.error);
|
|
19
|
+
reader.readAsDataURL(file);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function fileWidget(kind) {
|
|
23
|
+
return ({ form, name, field, id, document, labels = labels_1.WIDGET_LABELS.en }) => {
|
|
24
|
+
const element = document.createElement('div');
|
|
25
|
+
element.className = `fd-file fd-file-${kind}`;
|
|
26
|
+
const input = document.createElement('input');
|
|
27
|
+
input.type = 'file';
|
|
28
|
+
input.id = id;
|
|
29
|
+
input.className = 'fd-sr-only';
|
|
30
|
+
const accept = field.type === 'binary' ? field.accept : kind === 'image' ? ['image/*'] : undefined;
|
|
31
|
+
if (accept)
|
|
32
|
+
input.accept = accept.join(',');
|
|
33
|
+
const preview = document.createElement('img');
|
|
34
|
+
preview.className = 'fd-image-preview';
|
|
35
|
+
preview.alt = field.label;
|
|
36
|
+
preview.hidden = true;
|
|
37
|
+
const pick = document.createElement('label');
|
|
38
|
+
pick.className = kind === 'image' ? 'fd-file-pick fd-image-pick' : 'fd-file-pick';
|
|
39
|
+
pick.htmlFor = id;
|
|
40
|
+
const pickText = document.createElement('span');
|
|
41
|
+
pickText.className = 'fd-button';
|
|
42
|
+
pickText.textContent = kind === 'image' ? labels.uploadImage : labels.upload;
|
|
43
|
+
const hint = document.createElement('span');
|
|
44
|
+
hint.className = 'fd-help';
|
|
45
|
+
hint.textContent = labels.dropHere;
|
|
46
|
+
pick.append(pickText, hint);
|
|
47
|
+
const chosen = document.createElement('div');
|
|
48
|
+
chosen.className = 'fd-file-chosen';
|
|
49
|
+
const fileName = document.createElement('span');
|
|
50
|
+
fileName.className = 'fd-file-name';
|
|
51
|
+
const replace = document.createElement('button');
|
|
52
|
+
replace.type = 'button';
|
|
53
|
+
replace.className = 'fd-button fd-button-link';
|
|
54
|
+
replace.textContent = labels.replace;
|
|
55
|
+
replace.addEventListener('click', () => input.click());
|
|
56
|
+
const remove = document.createElement('button');
|
|
57
|
+
remove.type = 'button';
|
|
58
|
+
remove.className = 'fd-button fd-button-link';
|
|
59
|
+
remove.textContent = labels.removeFile;
|
|
60
|
+
remove.addEventListener('click', () => form.setValue(name, null));
|
|
61
|
+
chosen.append(fileName, replace, remove);
|
|
62
|
+
element.append(input, ...(kind === 'image' ? [preview] : []), pick, chosen);
|
|
63
|
+
let readonly = false;
|
|
64
|
+
const take = async (file) => {
|
|
65
|
+
if (!file || readonly)
|
|
66
|
+
return;
|
|
67
|
+
form.setValue(name, await read(file));
|
|
68
|
+
};
|
|
69
|
+
input.addEventListener('change', () => {
|
|
70
|
+
void take(input.files?.[0]);
|
|
71
|
+
input.value = '';
|
|
72
|
+
});
|
|
73
|
+
element.addEventListener('dragover', (event) => {
|
|
74
|
+
if (readonly)
|
|
75
|
+
return;
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
element.classList.add('fd-dragging');
|
|
78
|
+
});
|
|
79
|
+
element.addEventListener('dragleave', () => element.classList.remove('fd-dragging'));
|
|
80
|
+
element.addEventListener('drop', (event) => {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
element.classList.remove('fd-dragging');
|
|
83
|
+
void take(event.dataTransfer?.files?.[0]);
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
element,
|
|
87
|
+
focus: () => (pick.hidden ? replace : input).focus(),
|
|
88
|
+
update(state) {
|
|
89
|
+
readonly = state.readonly;
|
|
90
|
+
const file = state.value;
|
|
91
|
+
pick.hidden = !!file || readonly;
|
|
92
|
+
chosen.hidden = !file;
|
|
93
|
+
replace.hidden = remove.hidden = readonly;
|
|
94
|
+
input.disabled = readonly;
|
|
95
|
+
input.setAttribute('aria-invalid', String(state.invalid));
|
|
96
|
+
if (state.describedBy)
|
|
97
|
+
input.setAttribute('aria-describedby', state.describedBy);
|
|
98
|
+
fileName.textContent = file ? `${file.name} · ${(0, core_1.formatBytes)(file.size)}` : '';
|
|
99
|
+
if (kind === 'image') {
|
|
100
|
+
const src = file ? (file.data ? `data:${file.type};base64,${file.data}` : file.url ?? '') : '';
|
|
101
|
+
preview.hidden = !src;
|
|
102
|
+
if (preview.getAttribute('src') !== src) {
|
|
103
|
+
if (src)
|
|
104
|
+
preview.src = src;
|
|
105
|
+
else
|
|
106
|
+
preview.removeAttribute('src');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
exports.binaryWidget = fileWidget('binary');
|
|
114
|
+
exports.imageWidget = fileWidget('image');
|
|
115
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../../../../libs/widgets/src/lib/files.ts"],"names":[],"mappings":";;;AAAA,wCAA4D;AAC5D,qCAAyC;AAGzC;;;;GAIG;AAEH,SAAS,IAAI,CAAC,IAAU;IACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE;YACnB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;YACxC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACxG,CAAC,CAAC;QACF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,IAAwB;IAC1C,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,sBAAa,CAAC,EAAE,EAAE,EAAE,EAAE;QACxE,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,SAAS,GAAG,mBAAmB,IAAI,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;QACd,KAAK,CAAC,SAAS,GAAG,YAAY,CAAC;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACnG,IAAI,MAAM;YAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE5C,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,SAAS,GAAG,kBAAkB,CAAC;QACvC,OAAO,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;QAEtB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,cAAc,CAAC;QAClF,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAChD,QAAQ,CAAC,SAAS,GAAG,WAAW,CAAC;QACjC,QAAQ,CAAC,WAAW,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;QAC7E,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAE5B,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,SAAS,GAAG,gBAAgB,CAAC;QACpC,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAChD,QAAQ,CAAC,SAAS,GAAG,cAAc,CAAC;QACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;QACxB,OAAO,CAAC,SAAS,GAAG,0BAA0B,CAAC;QAC/C,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC;QACrC,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;QACvB,MAAM,CAAC,SAAS,GAAG,0BAA0B,CAAC;QAC9C,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;QACvC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAClE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAEzC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAE5E,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,MAAM,IAAI,GAAG,KAAK,EAAE,IAAsB,EAAE,EAAE;YAC5C,IAAI,CAAC,IAAI,IAAI,QAAQ;gBAAE,OAAO;YAC9B,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC;QACF,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;YACpC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5B,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;YAC7C,IAAI,QAAQ;gBAAE,OAAO;YACrB,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;QACrF,OAAO,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YACzC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACxC,KAAK,IAAI,CAAE,KAAmB,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,OAAO;YACP,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE;YACpD,MAAM,CAAC,KAAK;gBACV,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;gBAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAqC,CAAC;gBACzD,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,QAAQ,CAAC;gBACjC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC;gBACtB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;gBAC1C,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;gBAC1B,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC1D,IAAI,KAAK,CAAC,WAAW;oBAAE,KAAK,CAAC,YAAY,CAAC,kBAAkB,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;gBACjF,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,MAAM,IAAA,kBAAW,EAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9E,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;oBACrB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/F,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC;oBACtB,IAAI,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;wBACxC,IAAI,GAAG;4BAAE,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC;;4BACtB,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;oBACtC,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAEY,QAAA,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;AACpC,QAAA,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC"}
|