@teipublisher/pb-components 2.26.1-next.2 → 3.0.0-next-4.1
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/.github/workflows/docker-cypress.yml +54 -0
- package/.github/workflows/main.yml +6 -4
- package/.github/workflows/node.js.yml +56 -21
- package/.github/workflows/release.js.yml +19 -17
- package/.releaserc.json +1 -1
- package/CHANGELOG.md +351 -9
- package/Dockerfile +78 -70
- package/README.md +112 -4
- package/css/components.css +5 -5
- package/css/gridjs/mermaid.min.css +1 -1
- package/css/leaflet/Control.Geocoder.css +1 -126
- package/css/leaflet/images/layers.png +0 -0
- package/css/tify/tify.css +6 -5
- package/css/tom-select/tom-select.bootstrap4.min.css +1 -1
- package/css/tom-select/tom-select.bootstrap5.min.css +1 -1
- package/css/tom-select/tom-select.default.min.css +1 -1
- package/css/tom-select/tom-select.default.min.css.map +1 -0
- package/css/tom-select/tom-select.min.css +1 -1
- package/cypress.config.js +84 -0
- package/dist/api.html +1 -1
- package/dist/css/design-system.css +607 -0
- package/dist/demo/bundle-test.html +4 -3
- package/dist/demo/components.css +46 -1
- package/dist/demo/design-system.html +710 -0
- package/dist/demo/dts-client.html +2 -2
- package/dist/demo/pb-autocomplete.html +23 -11
- package/dist/demo/pb-autocomplete2.html +66 -55
- package/dist/demo/pb-autocomplete3.html +17 -8
- package/dist/demo/pb-blacklab-highlight.html +28 -11
- package/dist/demo/pb-blacklab-results.html +3 -2
- package/dist/demo/pb-browse-docs.html +24 -24
- package/dist/demo/pb-browse-docs2.html +3 -3
- package/dist/demo/pb-clipboard.html +32 -28
- package/dist/demo/pb-code-editor.html +6 -6
- package/dist/demo/pb-code-highlight.html +63 -63
- package/dist/demo/pb-codepen.html +1 -1
- package/dist/demo/pb-collapse.html +1 -1
- package/dist/demo/pb-collapse2.html +2 -2
- package/dist/demo/pb-combo-box.html +135 -130
- package/dist/demo/pb-custom-form.html +64 -55
- package/dist/demo/pb-dialog.html +12 -6
- package/dist/demo/pb-document.html +1 -1
- package/dist/demo/pb-download.html +68 -59
- package/dist/demo/pb-drawer.html +67 -46
- package/dist/demo/pb-drawer2.html +65 -58
- package/dist/demo/pb-edit-app.html +2 -2
- package/dist/demo/pb-edit-xml.html +1 -1
- package/dist/demo/pb-facsimile-2.html +26 -11
- package/dist/demo/pb-facsimile-3.html +25 -10
- package/dist/demo/pb-facsimile-dedup-test-2.html +48 -0
- package/dist/demo/pb-facsimile-dedup-test.html +48 -0
- package/dist/demo/pb-facsimile.html +4 -4
- package/dist/demo/pb-formula.html +1 -1
- package/dist/demo/pb-grid.html +22 -8
- package/dist/demo/pb-highlight.html +2 -2
- package/dist/demo/pb-i18n-simple.html +1 -0
- package/dist/demo/pb-i18n.html +15 -5
- package/dist/demo/pb-image-strip-standalone.html +2 -2
- package/dist/demo/pb-image-strip-view.html +2 -2
- package/dist/demo/pb-leaflet-map.html +3 -3
- package/dist/demo/pb-leaflet-map2.html +2 -2
- package/dist/demo/pb-leaflet-map3.html +3 -3
- package/dist/demo/pb-link.html +1 -1
- package/dist/demo/pb-load.html +2 -6
- package/dist/demo/pb-login.html +1 -3
- package/dist/demo/pb-manage-odds.html +9 -4
- package/dist/demo/pb-markdown.html +1 -1
- package/dist/demo/pb-media-query.html +2 -2
- package/dist/demo/pb-mei.html +2 -2
- package/dist/demo/pb-mei2.html +2 -2
- package/dist/demo/pb-message.html +2 -3
- package/dist/demo/pb-odd-editor.html +54 -52
- package/dist/demo/pb-page-header.html +27 -0
- package/dist/demo/pb-popover.html +1 -1
- package/dist/demo/pb-print-preview.html +2 -2
- package/dist/demo/pb-progress.html +4 -4
- package/dist/demo/pb-repeat.html +32 -36
- package/dist/demo/pb-search.html +16 -5
- package/dist/demo/pb-search2.html +4 -4
- package/dist/demo/pb-search3.html +3 -3
- package/dist/demo/pb-search4.html +3 -3
- package/dist/demo/pb-select-feature.html +4 -4
- package/dist/demo/pb-select-feature2.html +4 -4
- package/dist/demo/pb-select-feature3.html +2 -2
- package/dist/demo/pb-select-i18n.html +58 -53
- package/dist/demo/pb-select-odd.html +1 -1
- package/dist/demo/pb-select.html +190 -75
- package/dist/demo/pb-select2.html +91 -37
- package/dist/demo/pb-select3.html +109 -41
- package/dist/demo/pb-svg.html +1 -1
- package/dist/demo/pb-table-grid.html +26 -15
- package/dist/demo/pb-tabs.html +15 -7
- package/dist/demo/pb-tify.html +7 -7
- package/dist/demo/pb-timeline.html +1 -1
- package/dist/demo/pb-timeline2.html +1 -1
- package/dist/demo/pb-toggle-feature.html +26 -23
- package/dist/demo/pb-toggle-feature2.html +4 -4
- package/dist/demo/pb-toggle-feature3.html +2 -2
- package/dist/demo/pb-toggle-feature4.html +56 -54
- package/dist/demo/pb-version.html +2 -2
- package/dist/demo/pb-view.html +78 -40
- package/dist/demo/pb-view2.html +69 -46
- package/dist/demo/pb-view3.html +53 -48
- package/dist/demo/pb-view4.html +70 -49
- package/dist/demo/pb-zoom.html +2 -2
- package/dist/{es-global-bridge-d8ce175d.js → es-global-bridge-D8ZcUcx_.js} +0 -4
- package/dist/focus-mixin-VCsFap6b.js +768 -0
- package/dist/images/icons.svg +217 -0
- package/dist/jinn-codemirror-DETLdm08.js +1 -0
- package/dist/lib/openseadragon.min.js +80 -0
- package/dist/lib/openseadragon.min.js.map +1 -0
- package/dist/pb-code-editor.js +25 -20
- package/dist/pb-component-docs.js +414 -3225
- package/dist/pb-components-bundle.js +3046 -4402
- package/dist/pb-dialog-tklYGWfc.js +121 -0
- package/dist/pb-edit-app.js +208 -107
- package/dist/pb-elements.json +716 -249
- package/dist/pb-facsimile.js +46 -0
- package/dist/pb-i18n-C0NDma4h.js +1 -0
- package/dist/pb-leaflet-map.js +23 -23
- package/dist/pb-mei.js +152 -134
- package/dist/pb-mixin-DHoWQheB.js +1 -0
- package/dist/pb-odd-editor.js +1671 -1231
- package/dist/pb-tify.js +1 -27
- package/dist/unsafe-html-D5VGo9Oq.js +1 -0
- package/dist/urls-BEONu_g4.js +1 -0
- package/eslint.config.mjs +92 -0
- package/gh-pages.js +5 -3
- package/i18n/common/en.json +6 -0
- package/i18n/common/pl.json +2 -2
- package/images/icons.svg +217 -0
- package/index.html +0 -5
- package/lib/leaflet-src.js.map +1 -0
- package/lib/leaflet.markercluster-src.js.map +1 -0
- package/lib/openseadragon.min.js +6 -6
- package/package.json +56 -81
- package/pb-elements.json +716 -249
- package/rollup.config.mjs +312 -0
- package/src/assets/components.css +5 -5
- package/src/assets/design-system.css +607 -0
- package/src/authority/airtable.js +20 -21
- package/src/authority/anton.js +129 -129
- package/src/authority/custom.js +70 -27
- package/src/authority/geonames.js +38 -32
- package/src/authority/gnd.js +50 -42
- package/src/authority/kbga.js +136 -134
- package/src/authority/metagrid.js +44 -46
- package/src/authority/reconciliation.js +66 -68
- package/src/authority/registry.js +4 -4
- package/src/docs/demo-utils.js +91 -0
- package/src/docs/pb-component-docs.js +287 -147
- package/src/docs/pb-component-view.js +380 -273
- package/src/docs/pb-components-list.js +115 -51
- package/src/docs/pb-demo-snippet.js +199 -174
- package/src/dts-client.js +306 -303
- package/src/dts-select-endpoint.js +125 -85
- package/src/parse-date-service.js +184 -135
- package/src/pb-ajax.js +175 -173
- package/src/pb-authority-lookup.js +198 -158
- package/src/pb-autocomplete.js +731 -313
- package/src/pb-blacklab-highlight.js +266 -260
- package/src/pb-blacklab-results.js +230 -225
- package/src/pb-browse-docs.js +601 -484
- package/src/pb-browse.js +68 -65
- package/src/pb-clipboard.js +97 -76
- package/src/pb-code-editor.js +111 -103
- package/src/pb-code-highlight.js +234 -204
- package/src/pb-codepen.js +81 -73
- package/src/pb-collapse.js +265 -152
- package/src/pb-combo-box.js +191 -191
- package/src/pb-components-bundle.js +1 -7
- package/src/pb-components.js +2 -6
- package/src/pb-custom-form.js +230 -141
- package/src/pb-dialog.js +99 -63
- package/src/pb-document.js +118 -91
- package/src/pb-download.js +214 -198
- package/src/pb-drawer.js +146 -149
- package/src/pb-edit-app.js +471 -240
- package/src/pb-edit-xml.js +101 -98
- package/src/pb-events.js +126 -107
- package/src/pb-facs-link.js +130 -101
- package/src/pb-facsimile.js +494 -410
- package/src/pb-fetch.js +389 -0
- package/src/pb-formula.js +152 -154
- package/src/pb-geolocation.js +130 -132
- package/src/pb-grid-action.js +59 -56
- package/src/pb-grid.js +388 -228
- package/src/pb-highlight.js +142 -142
- package/src/pb-hotkeys.js +40 -42
- package/src/pb-i18n.js +115 -127
- package/src/pb-icon-button.js +108 -0
- package/src/pb-icon.js +283 -0
- package/src/pb-image-strip.js +85 -79
- package/src/pb-lang.js +142 -57
- package/src/pb-leaflet-map.js +551 -483
- package/src/pb-link.js +132 -126
- package/src/pb-load.js +495 -428
- package/src/pb-login.js +303 -248
- package/src/pb-manage-odds.js +384 -338
- package/src/pb-map-icon.js +90 -90
- package/src/pb-map-layer.js +86 -86
- package/src/pb-markdown.js +107 -110
- package/src/pb-media-query.js +75 -73
- package/src/pb-mei.js +523 -303
- package/src/pb-message.js +144 -98
- package/src/pb-mixin.js +268 -265
- package/src/pb-navigation.js +83 -96
- package/src/pb-observable.js +39 -39
- package/src/pb-odd-editor.js +1209 -948
- package/src/pb-odd-elementspec-editor.js +375 -310
- package/src/pb-odd-model-editor.js +1189 -941
- package/src/pb-odd-parameter-editor.js +269 -170
- package/src/pb-odd-rendition-editor.js +184 -131
- package/src/pb-page.js +451 -422
- package/src/pb-paginate.js +260 -178
- package/src/pb-panel.js +217 -183
- package/src/pb-popover-themes.js +16 -9
- package/src/pb-popover.js +297 -288
- package/src/pb-print-preview.js +128 -128
- package/src/pb-progress.js +52 -52
- package/src/pb-repeat.js +141 -108
- package/src/pb-restricted.js +85 -78
- package/src/pb-search.js +258 -230
- package/src/pb-select-feature.js +210 -126
- package/src/pb-select-odd.js +184 -118
- package/src/pb-select-template.js +113 -78
- package/src/pb-select.js +330 -229
- package/src/pb-split-list.js +181 -176
- package/src/pb-svg.js +81 -80
- package/src/pb-table-column.js +55 -55
- package/src/pb-table-grid.js +334 -205
- package/src/pb-tabs.js +238 -61
- package/src/pb-tify.js +3331 -126
- package/src/pb-timeline.js +394 -255
- package/src/pb-toggle-feature.js +196 -188
- package/src/pb-upload.js +201 -176
- package/src/pb-version.js +22 -34
- package/src/pb-view-annotate.js +138 -102
- package/src/pb-view.js +1722 -1272
- package/src/pb-zoom.js +144 -46
- package/src/search-result-service.js +256 -223
- package/src/seed-element.js +14 -22
- package/src/settings.js +4 -4
- package/src/theming.js +98 -91
- package/src/urls.js +403 -289
- package/src/utils.js +53 -51
- package/vite.config.js +86 -0
- package/css/pb-styles.css +0 -51
- package/dist/iron-form-3b8dcaa7.js +0 -210
- package/dist/jinn-codemirror-da0e2d1f.js +0 -1
- package/dist/paper-checkbox-515a5284.js +0 -1597
- package/dist/paper-icon-button-b1d31571.js +0 -398
- package/dist/paper-listbox-a3b7175c.js +0 -1265
- package/dist/pb-i18n-0611135a.js +0 -1
- package/dist/pb-mixin-b1caa22e.js +0 -158
- package/dist/polymer-hack.js +0 -1
- package/dist/vaadin-element-mixin-6e4cee3a.js +0 -527
- package/lib/Control.Geocoder.min.js +0 -2
- package/lib/Control.Geocoder.min.js.map +0 -1
- package/src/assets/pb-styles.css +0 -51
- package/src/pb-light-dom.js +0 -40
- package/src/polymer-hack.js +0 -6
package/src/pb-i18n.js
CHANGED
|
@@ -1,167 +1,155 @@
|
|
|
1
|
-
import { LitElement, html } from 'lit
|
|
2
|
-
import { directive, NodePart, AttributePart } from "lit-html";
|
|
3
|
-
|
|
4
|
-
// Cache created lit-html parts, so we can update translations
|
|
5
|
-
const partCache = new Map();
|
|
1
|
+
import { LitElement, html } from 'lit';
|
|
6
2
|
|
|
7
3
|
// The currently used i18next translation function
|
|
8
|
-
let
|
|
4
|
+
let _translateFn;
|
|
5
|
+
// Bump whenever the translation function or resources change
|
|
6
|
+
let _i18nVersion = 0;
|
|
7
|
+
// Shared, tiny in-memory cache per-version (key+options)
|
|
8
|
+
const _i18nCache = new Map();
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
11
|
* Called by pb-page before the first pb-i18n-update
|
|
12
12
|
* to make sure the translation function is set.
|
|
13
13
|
*/
|
|
14
14
|
export function initTranslation(translate) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function isConnected(part) {
|
|
19
|
-
if (part instanceof NodePart) {
|
|
20
|
-
return part.startNode.isConnected;
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
if (part instanceof AttributePart) {
|
|
24
|
-
return part.committer.element.isConnected;
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
return part.element.isConnected;
|
|
15
|
+
_translateFn = translate;
|
|
16
|
+
_i18nVersion++;
|
|
17
|
+
_i18nCache.clear();
|
|
28
18
|
}
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
Object.keys(partCache).forEach((part) => {
|
|
32
|
-
if (!isConnected(part)) {
|
|
33
|
-
partCache.delete(part);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function whenIdle(cb) {
|
|
39
|
-
"requestIdleCallback" in window ? window.requestIdleCallback(cb) : setTimeout(cb)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function updatePart(part, cb) {
|
|
43
|
-
|
|
44
|
-
// Grab the new value
|
|
45
|
-
const newValue = cb();
|
|
46
|
-
|
|
47
|
-
// Only set the value if it has changed
|
|
48
|
-
if (part.value === newValue) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Set the new value
|
|
53
|
-
part.setValue(newValue);
|
|
54
|
-
part.commit();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function updateParts(options) {
|
|
58
|
-
_translate = options.t;
|
|
59
|
-
partCache.forEach((cb, part) => {
|
|
60
|
-
if (isConnected(part)) {
|
|
61
|
-
updatePart(part, cb);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
20
|
+
// No-op helpers kept for backward compatibility of API surface
|
|
65
21
|
|
|
66
22
|
/**
|
|
67
23
|
* Translate the given string using i18n.
|
|
68
|
-
*
|
|
24
|
+
*
|
|
69
25
|
* @param {String} key The key to translate
|
|
70
26
|
* @param {Object} [value] Optional object containing interpolation values
|
|
71
|
-
* @returns
|
|
27
|
+
* @returns {string}
|
|
72
28
|
*/
|
|
73
29
|
export function get(key, value) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
return key;
|
|
30
|
+
if (_translateFn) return _translateFn(key, value);
|
|
31
|
+
return key;
|
|
78
32
|
}
|
|
79
33
|
|
|
80
|
-
export const langChanged = directive((cb) => (part) => {
|
|
81
|
-
partCache.set(part, cb);
|
|
82
|
-
updatePart(part, cb);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
34
|
/**
|
|
86
35
|
* lit-html directive to translate a given key into the target language
|
|
87
36
|
* using i18next.
|
|
88
|
-
*
|
|
89
|
-
* @param {String} key
|
|
90
|
-
* @param {Object} [value]
|
|
37
|
+
*
|
|
38
|
+
* @param {String} key
|
|
39
|
+
* @param {Object} [value]
|
|
91
40
|
*/
|
|
92
|
-
export const translate = (key, value) =>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
41
|
+
export const translate = (key, value) => get(key, value);
|
|
42
|
+
|
|
43
|
+
// Keep _translateFn in sync on updates (guard for non-browser envs)
|
|
44
|
+
if (typeof document !== 'undefined') {
|
|
45
|
+
document.addEventListener('pb-i18n-update', ev => {
|
|
46
|
+
const next = ev.detail?.t;
|
|
47
|
+
if (next && next !== _translateFn) {
|
|
48
|
+
_translateFn = next;
|
|
49
|
+
}
|
|
50
|
+
// Invalidate cache regardless (resources may have changed)
|
|
51
|
+
_i18nVersion++;
|
|
52
|
+
_i18nCache.clear();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
100
55
|
|
|
101
56
|
/**
|
|
102
57
|
* Insert translated text somewhere on an HTML page. If no translation is found,
|
|
103
58
|
* display the contained content.
|
|
104
59
|
*/
|
|
105
60
|
export class PbI18n extends LitElement {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
61
|
+
static get properties() {
|
|
62
|
+
return {
|
|
63
|
+
...super.properties,
|
|
64
|
+
/**
|
|
65
|
+
* The i18n key to use for looking up the translation.
|
|
66
|
+
*/
|
|
67
|
+
key: { type: String },
|
|
68
|
+
/**
|
|
69
|
+
* Optional interpolation parameters to be passed to the
|
|
70
|
+
* translation function
|
|
71
|
+
*/
|
|
72
|
+
options: { type: Object },
|
|
73
|
+
_translated: { type: String },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
constructor() {
|
|
78
|
+
super();
|
|
79
|
+
this.key = 'missing-key';
|
|
80
|
+
this.options = null;
|
|
81
|
+
this._translated = null;
|
|
82
|
+
this._fallback = '';
|
|
83
|
+
// stable handler so we can remove it on disconnect
|
|
84
|
+
this._onI18nUpdate = () => this._recompute();
|
|
85
|
+
this._recomputeTimer = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
connectedCallback() {
|
|
89
|
+
super.connectedCallback();
|
|
90
|
+
|
|
91
|
+
// Capture initial fallback then clear host content so Lit fully controls light DOM
|
|
92
|
+
if (!this._capturedFallback) {
|
|
93
|
+
this._fallback = this.innerHTML;
|
|
94
|
+
this._capturedFallback = true;
|
|
126
95
|
}
|
|
96
|
+
this.innerHTML = '';
|
|
127
97
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.key = 'missing-key';
|
|
131
|
-
this._options = null;
|
|
132
|
-
this._translated = null;
|
|
98
|
+
if (typeof document !== 'undefined') {
|
|
99
|
+
document.addEventListener('pb-i18n-update', this._onI18nUpdate);
|
|
133
100
|
}
|
|
134
101
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this._fallback = this.innerHTML;
|
|
139
|
-
|
|
140
|
-
document.addEventListener('pb-i18n-update', this._translate.bind(this));
|
|
102
|
+
this._scheduleRecompute();
|
|
103
|
+
}
|
|
141
104
|
|
|
142
|
-
|
|
105
|
+
disconnectedCallback() {
|
|
106
|
+
if (typeof document !== 'undefined') {
|
|
107
|
+
document.removeEventListener('pb-i18n-update', this._onI18nUpdate);
|
|
143
108
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this._translate();
|
|
109
|
+
if (this._recomputeTimer) {
|
|
110
|
+
clearTimeout(this._recomputeTimer);
|
|
111
|
+
this._recomputeTimer = null;
|
|
148
112
|
}
|
|
113
|
+
super.disconnectedCallback();
|
|
114
|
+
}
|
|
149
115
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this._translated = transl;
|
|
154
|
-
} else {
|
|
155
|
-
this._translated = null;
|
|
156
|
-
}
|
|
116
|
+
updated(changed) {
|
|
117
|
+
if (changed.has('key') || changed.has('options')) {
|
|
118
|
+
this._scheduleRecompute();
|
|
157
119
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_scheduleRecompute() {
|
|
123
|
+
if (this._recomputeTimer) return;
|
|
124
|
+
// Small debounce to coalesce multiple updates and event bursts
|
|
125
|
+
this._recomputeTimer = setTimeout(() => {
|
|
126
|
+
this._recomputeTimer = null;
|
|
127
|
+
this._recompute();
|
|
128
|
+
}, 20);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_recompute() {
|
|
132
|
+
const opts = this.options || {};
|
|
133
|
+
const cacheKey = `${_i18nVersion}::${this.key}::${JSON.stringify(opts)}`;
|
|
134
|
+
if (_i18nCache.has(cacheKey)) {
|
|
135
|
+
this._translated = _i18nCache.get(cacheKey);
|
|
136
|
+
return;
|
|
161
137
|
}
|
|
162
138
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
139
|
+
const result = get(this.key, opts);
|
|
140
|
+
const value = result && result !== this.key ? result : null;
|
|
141
|
+
_i18nCache.set(cacheKey, value);
|
|
142
|
+
this._translated = value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
render() {
|
|
146
|
+
// Return text (not HTML) so translations aren’t treated as markup
|
|
147
|
+
return this._translated ?? this._fallback;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// light DOM rendering (as before)
|
|
151
|
+
createRenderRoot() {
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
166
154
|
}
|
|
167
|
-
customElements.define('pb-i18n', PbI18n);
|
|
155
|
+
customElements.define('pb-i18n', PbI18n);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit';
|
|
2
|
+
import './pb-icon.js';
|
|
3
|
+
|
|
4
|
+
const normalizeLabel = (value = '') => value.replace(/[-_]/g, ' ').trim();
|
|
5
|
+
|
|
6
|
+
export class PbIconButton extends LitElement {
|
|
7
|
+
static properties = {
|
|
8
|
+
icon: { type: String },
|
|
9
|
+
disabled: { type: Boolean, reflect: true },
|
|
10
|
+
label: { type: String },
|
|
11
|
+
toggles: { type: Boolean },
|
|
12
|
+
active: { type: Boolean, reflect: true },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
this.icon = '';
|
|
18
|
+
this.disabled = false;
|
|
19
|
+
this.label = '';
|
|
20
|
+
this.toggles = false;
|
|
21
|
+
this.active = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
render() {
|
|
25
|
+
const hostTitle = this.title || '';
|
|
26
|
+
const iconLabel = this.icon
|
|
27
|
+
? normalizeLabel(this.icon.includes(':') ? this.icon.split(':').pop() : this.icon)
|
|
28
|
+
: '';
|
|
29
|
+
const ariaLabel = this.label || hostTitle || iconLabel;
|
|
30
|
+
|
|
31
|
+
return html`
|
|
32
|
+
<button
|
|
33
|
+
class="pb-button pb-button--icon"
|
|
34
|
+
type="button"
|
|
35
|
+
title=${hostTitle}
|
|
36
|
+
aria-label=${ariaLabel}
|
|
37
|
+
?disabled=${this.disabled}
|
|
38
|
+
aria-pressed=${this.toggles ? String(this.active) : undefined}
|
|
39
|
+
@click=${this._handleClick}
|
|
40
|
+
>
|
|
41
|
+
${this.icon
|
|
42
|
+
? html`<pb-icon icon="${this.icon}" decorative></pb-icon>`
|
|
43
|
+
: html`<slot></slot>`}
|
|
44
|
+
</button>
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_handleClick(event) {
|
|
49
|
+
if (this.disabled) {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
event.stopImmediatePropagation();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.toggles) {
|
|
56
|
+
this.active = !this.active;
|
|
57
|
+
this.dispatchEvent(
|
|
58
|
+
new CustomEvent('active-changed', {
|
|
59
|
+
detail: { value: this.active },
|
|
60
|
+
bubbles: true,
|
|
61
|
+
composed: true,
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static styles = css`
|
|
68
|
+
:host {
|
|
69
|
+
display: inline-flex;
|
|
70
|
+
box-sizing: border-box;
|
|
71
|
+
--pb-icon-button-size: 40px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
button {
|
|
75
|
+
width: var(--pb-icon-button-size);
|
|
76
|
+
height: var(--pb-icon-button-size);
|
|
77
|
+
border-radius: 999px;
|
|
78
|
+
border: none;
|
|
79
|
+
background: transparent;
|
|
80
|
+
color: inherit;
|
|
81
|
+
padding: 0;
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
justify-content: center;
|
|
86
|
+
transition: background 0.2s ease-in-out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
button:hover:not(:disabled),
|
|
90
|
+
button:focus-visible {
|
|
91
|
+
background: var(--pb-icon-button-hover-background, rgba(0, 0, 0, 0.05));
|
|
92
|
+
outline: none;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
button:disabled {
|
|
96
|
+
opacity: 0.4;
|
|
97
|
+
cursor: not-allowed;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:host([active]) button {
|
|
101
|
+
background: var(--pb-icon-button-active-background, rgba(0, 0, 0, 0.08));
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!customElements.get('pb-icon-button')) {
|
|
107
|
+
customElements.define('pb-icon-button', PbIconButton);
|
|
108
|
+
}
|
package/src/pb-icon.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit';
|
|
2
|
+
|
|
3
|
+
if (!customElements.__pbDefineGuard) {
|
|
4
|
+
const originalDefine = customElements.define.bind(customElements);
|
|
5
|
+
customElements.define = (name, constructor, options) => {
|
|
6
|
+
if (customElements.get(name)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
originalDefine(name, constructor, options);
|
|
10
|
+
};
|
|
11
|
+
customElements.__pbDefineGuard = true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const polymerIconMap = {
|
|
15
|
+
'icons:file-upload': 'upload',
|
|
16
|
+
'icons:file-download': 'download',
|
|
17
|
+
'icons:cloud-download': 'download',
|
|
18
|
+
'icons:clear': 'close',
|
|
19
|
+
'icons:check': 'check',
|
|
20
|
+
'icons:error-outline': 'warning',
|
|
21
|
+
'icons:folder-open': 'folder-open',
|
|
22
|
+
'icons:arrow-upward': 'arrow-upward',
|
|
23
|
+
'icons:arrow-forward': 'chevron-right',
|
|
24
|
+
'icons:arrow-back': 'chevron-left',
|
|
25
|
+
'icons:chevron-right': 'chevron-right',
|
|
26
|
+
'icons:chevron-left': 'chevron-left',
|
|
27
|
+
'icons:open-in-new': 'open-in-new',
|
|
28
|
+
'icons:flag': 'flag',
|
|
29
|
+
'icons:code': 'code',
|
|
30
|
+
'icons:hourglass-empty': 'clock',
|
|
31
|
+
'icons:delete': 'delete',
|
|
32
|
+
'icons:create': 'edit',
|
|
33
|
+
'icons:expand-more': 'expand-more',
|
|
34
|
+
'icons:expand-less': 'expand-less',
|
|
35
|
+
'icons:view-list': 'view-list',
|
|
36
|
+
'icons:zoom-in': 'zoom-in',
|
|
37
|
+
'icons:zoom-out': 'zoom-out',
|
|
38
|
+
'icons:content-copy': 'copy',
|
|
39
|
+
'content-copy': 'copy',
|
|
40
|
+
'icons:content-paste': 'content-paste',
|
|
41
|
+
'content-paste': 'content-paste',
|
|
42
|
+
'icons:arrow-downward': 'arrow-downward',
|
|
43
|
+
'icons:refresh': 'refresh',
|
|
44
|
+
'icons:save': 'save',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A versatile icon component that replaces iron-icon and iron-icons.
|
|
49
|
+
* Supports both SVG sprite references and inline SVG content.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* <!-- Using icon name (requires icon sprite) -->
|
|
53
|
+
* <pb-icon icon="expand-more"></pb-icon>
|
|
54
|
+
*
|
|
55
|
+
* <!-- Using external sprite (default dist/images/icons.svg) -->
|
|
56
|
+
* <pb-icon icon="expand-more" sprite="dist/images/icons.svg"></pb-icon>
|
|
57
|
+
*
|
|
58
|
+
* <!-- Using inline SVG -->
|
|
59
|
+
* <pb-icon>
|
|
60
|
+
* <svg viewBox="0 0 24 24">
|
|
61
|
+
* <path d="M7 10l5 5 5-5z"/>
|
|
62
|
+
* </svg>
|
|
63
|
+
* </pb-icon>
|
|
64
|
+
*
|
|
65
|
+
* @slot - Inline SVG content when not using icon name
|
|
66
|
+
* @cssprop [--pb-icon-color] - Icon color (defaults to currentColor)
|
|
67
|
+
* @cssprop [--pb-icon-size] - Icon size (defaults to 1em)
|
|
68
|
+
*/
|
|
69
|
+
export class PbIcon extends LitElement {
|
|
70
|
+
static get properties() {
|
|
71
|
+
return {
|
|
72
|
+
/**
|
|
73
|
+
* Icon name to display from the icon sprite.
|
|
74
|
+
* If not provided, expects SVG content in the default slot.
|
|
75
|
+
*/
|
|
76
|
+
icon: { type: String },
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Path or URL to the SVG sprite file. Optional.
|
|
80
|
+
* If not specified, defaults to 'dist/images/icons.svg'.
|
|
81
|
+
* If set to a string starting with '#', uses inline sprite.
|
|
82
|
+
*/
|
|
83
|
+
sprite: { type: String },
|
|
84
|
+
/**
|
|
85
|
+
* Icon size. Can be a preset (xs, sm, md, lg, xl) or a custom size.
|
|
86
|
+
*/
|
|
87
|
+
size: { type: String },
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* ARIA label for accessibility. Auto-generated from icon name if not provided.
|
|
91
|
+
*/
|
|
92
|
+
label: { type: String },
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Whether the icon is decorative (hidden from screen readers).
|
|
96
|
+
*/
|
|
97
|
+
decorative: { type: Boolean },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
constructor() {
|
|
102
|
+
super();
|
|
103
|
+
this.icon = '';
|
|
104
|
+
this.size = '';
|
|
105
|
+
this.label = '';
|
|
106
|
+
this.decorative = false;
|
|
107
|
+
// Default sprite path: try to determine the correct base URL
|
|
108
|
+
try {
|
|
109
|
+
const isDev =
|
|
110
|
+
(typeof import.meta !== 'undefined' && import.meta.env && import.meta.env.DEV) ||
|
|
111
|
+
(typeof location !== 'undefined' && /localhost|127\.0\.0\.1/.test(location.hostname));
|
|
112
|
+
|
|
113
|
+
if (isDev) {
|
|
114
|
+
// In dev mode, check if we're running in TEI Publisher context
|
|
115
|
+
const currentHost = window.location.hostname;
|
|
116
|
+
const currentPort = window.location.port;
|
|
117
|
+
|
|
118
|
+
// If we're on the TEI Publisher port (8080), use the app's resources path
|
|
119
|
+
if (currentPort === '8080' || currentHost.includes('tei-publisher')) {
|
|
120
|
+
this.sprite = '/exist/apps/tei-publisher/resources/images/icons.svg';
|
|
121
|
+
} else {
|
|
122
|
+
// Otherwise use the Vite dev server path
|
|
123
|
+
this.sprite = '/images/icons.svg';
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
this.sprite = '/dist/images/icons.svg';
|
|
127
|
+
}
|
|
128
|
+
} catch (_) {
|
|
129
|
+
this.sprite = '/dist/images/icons.svg';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
render() {
|
|
134
|
+
const sizeClass = this._getSizeClass();
|
|
135
|
+
const ariaLabel = this._getAriaLabel();
|
|
136
|
+
|
|
137
|
+
return html`
|
|
138
|
+
<span
|
|
139
|
+
class="pb-icon ${sizeClass}"
|
|
140
|
+
role="img"
|
|
141
|
+
aria-label="${ariaLabel}"
|
|
142
|
+
aria-hidden="${this.decorative ? 'true' : 'false'}"
|
|
143
|
+
>
|
|
144
|
+
${this.icon ? this._renderSpriteIcon() : this._renderSlotIcon()}
|
|
145
|
+
</span>
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_renderSpriteIcon() {
|
|
150
|
+
const iconName = this._normalizeIconName(this.icon);
|
|
151
|
+
if (!iconName) {
|
|
152
|
+
return html``;
|
|
153
|
+
}
|
|
154
|
+
const href =
|
|
155
|
+
this.sprite && !this.sprite.startsWith('#')
|
|
156
|
+
? `${this.sprite}#icon-${iconName}`
|
|
157
|
+
: `#icon-${iconName}`;
|
|
158
|
+
return html`
|
|
159
|
+
<svg
|
|
160
|
+
width="1em"
|
|
161
|
+
height="1em"
|
|
162
|
+
viewBox="0 0 24 24"
|
|
163
|
+
aria-hidden="${this.decorative ? 'true' : 'false'}"
|
|
164
|
+
>
|
|
165
|
+
<use href="${href}"></use>
|
|
166
|
+
</svg>
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
_renderSlotIcon() {
|
|
171
|
+
return html`<slot></slot>`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
_normalizeIconName(name) {
|
|
175
|
+
if (!name) return '';
|
|
176
|
+
const lower = name.toLowerCase();
|
|
177
|
+
if (polymerIconMap[lower]) {
|
|
178
|
+
return polymerIconMap[lower];
|
|
179
|
+
}
|
|
180
|
+
if (lower.includes(':')) {
|
|
181
|
+
return lower.split(':').pop();
|
|
182
|
+
}
|
|
183
|
+
return lower;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
_getSizeClass() {
|
|
187
|
+
if (!this.size) return '';
|
|
188
|
+
|
|
189
|
+
const presetSizes = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
190
|
+
if (presetSizes.includes(this.size)) {
|
|
191
|
+
return `pb-icon--${this.size}`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Custom size - set CSS custom property
|
|
195
|
+
this.style.setProperty('--pb-icon-size', this.size);
|
|
196
|
+
return '';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_getAriaLabel() {
|
|
200
|
+
if (this.decorative) return '';
|
|
201
|
+
if (this.label) return this.label;
|
|
202
|
+
if (this.icon) {
|
|
203
|
+
const normalized = this._normalizeIconName(this.icon);
|
|
204
|
+
return normalized.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
205
|
+
}
|
|
206
|
+
return 'Icon';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
static get styles() {
|
|
210
|
+
return css`
|
|
211
|
+
:host {
|
|
212
|
+
display: inline-flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.pb-icon {
|
|
218
|
+
display: inline-flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
justify-content: center;
|
|
221
|
+
width: var(--pb-icon-size, 1em);
|
|
222
|
+
height: var(--pb-icon-size, 1em);
|
|
223
|
+
color: var(--pb-icon-color, currentColor);
|
|
224
|
+
fill: currentColor;
|
|
225
|
+
flex-shrink: 0;
|
|
226
|
+
user-select: none;
|
|
227
|
+
line-height: 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.pb-icon svg {
|
|
231
|
+
width: 100%;
|
|
232
|
+
height: 100%;
|
|
233
|
+
fill: inherit;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Size variants */
|
|
237
|
+
.pb-icon--xs {
|
|
238
|
+
font-size: 12px;
|
|
239
|
+
width: 12px;
|
|
240
|
+
height: 12px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.pb-icon--sm {
|
|
244
|
+
font-size: 16px;
|
|
245
|
+
width: 16px;
|
|
246
|
+
height: 16px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.pb-icon--md {
|
|
250
|
+
font-size: 20px;
|
|
251
|
+
width: 20px;
|
|
252
|
+
height: 20px;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.pb-icon--lg {
|
|
256
|
+
font-size: 24px;
|
|
257
|
+
width: 24px;
|
|
258
|
+
height: 24px;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.pb-icon--xl {
|
|
262
|
+
font-size: 32px;
|
|
263
|
+
width: 32px;
|
|
264
|
+
height: 32px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Ensure slotted SVGs are properly sized */
|
|
268
|
+
::slotted(svg) {
|
|
269
|
+
width: 100%;
|
|
270
|
+
height: 100%;
|
|
271
|
+
fill: currentColor;
|
|
272
|
+
}
|
|
273
|
+
`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!customElements.get('pb-icon')) {
|
|
278
|
+
customElements.define('pb-icon', PbIcon);
|
|
279
|
+
}
|
|
280
|
+
if (!customElements.get('iron-icon')) {
|
|
281
|
+
class PbIronIcon extends PbIcon {}
|
|
282
|
+
customElements.define('iron-icon', PbIronIcon);
|
|
283
|
+
}
|