@wpmoo/ui 1.5.0 → 2.0.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 +0 -28
- package/README.md +47 -10
- package/THIRD_PARTY_NOTICES.md +87 -0
- package/certification.json +2 -1
- package/dist/assets/css/moo-ui.css +1 -1
- package/dist/assets/css/moo-ui.min.css +1 -1
- package/dist/assets/css/moo.css +1 -1
- package/dist/assets/css/moo.min.css +1 -1
- package/dist/js/chart.js +102 -14600
- package/dist/js/chart.min.js +2 -22
- package/dist/js/combobox.js +1 -1
- package/dist/js/context-menu.js +1 -1
- package/dist/js/datatable.js +1 -1
- package/dist/js/datepicker.js +1 -1
- package/dist/js/datepicker.min.js +1 -1
- package/dist/js/floating-panel.js +1 -1
- package/dist/js/moo-ui.js +4 -1
- package/dist/js/moo-ui.min.js +2 -2
- package/dist/js/overlay-scrollbar.js +1 -1
- package/dist/js/password-requirements.js +257 -0
- package/dist/js/sheet.js +1 -1
- package/dist/js/sidebar.js +1 -1
- package/dist/js/slider.js +1 -1
- package/dist/js/state.js +1 -1
- package/dist/js/table-of-contents.js +1 -1
- package/dist/js/toc.js +1 -1
- package/dist/release-manifest.json +4 -4
- package/package.json +10 -4
- package/scss/mixins/_banner.scss +1 -1
- package/ASSET_LICENSE.md +0 -19
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI password-requirements.js v2.0.0 (https://wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo Authors
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
function strip(value, characters) {
|
|
9
|
+
const points = Array.from(value);
|
|
10
|
+
let start = 0;
|
|
11
|
+
let end = points.length;
|
|
12
|
+
while (start < end && characters.includes(points[start])) start++;
|
|
13
|
+
while (end > start && characters.includes(points[end - 1])) end--;
|
|
14
|
+
return points.slice(start, end).join("");
|
|
15
|
+
}
|
|
16
|
+
function passwordRequirementStates(value, policy) {
|
|
17
|
+
const normalized = strip(value.normalize("NFKC"), policy.whitespace);
|
|
18
|
+
const maximum = Math.max(Array.from(value).length, Array.from(value.normalize("NFKC")).length) <= policy.maxlength;
|
|
19
|
+
const checks = {
|
|
20
|
+
length: Math.min(Array.from(strip(value, policy.whitespace)).length, Array.from(normalized).length) >= policy.minlength,
|
|
21
|
+
case: /\p{Uppercase}/u.test(normalized) && /\p{Lowercase}/u.test(normalized),
|
|
22
|
+
number: /\p{Nd}/u.test(normalized),
|
|
23
|
+
symbol: /[\p{P}\p{S}]/u.test(normalized),
|
|
24
|
+
maximum
|
|
25
|
+
};
|
|
26
|
+
return policy.rules.map((rule) => ({ key: rule.key, label: rule.label, warning: Boolean(rule.warning), met: Boolean(checks[rule.key]) }));
|
|
27
|
+
}
|
|
28
|
+
class PasswordRequirements {
|
|
29
|
+
static getInstance(element) {
|
|
30
|
+
return instances.get(element) || null;
|
|
31
|
+
}
|
|
32
|
+
static getOrCreateInstance(element, options) {
|
|
33
|
+
return this.getInstance(element) || new this(element, options);
|
|
34
|
+
}
|
|
35
|
+
constructor(element, options = {}) {
|
|
36
|
+
if (!element?.matches("fieldset[data-password-requirements]") || !element.id) {
|
|
37
|
+
throw new TypeError("PasswordRequirements requires an identified Fieldset.");
|
|
38
|
+
}
|
|
39
|
+
const existing = instances.get(element);
|
|
40
|
+
if (existing) return existing;
|
|
41
|
+
const configuration = JSON.parse(element.dataset.passwordRequirements);
|
|
42
|
+
const document = element.ownerDocument;
|
|
43
|
+
const owner = element.closest(".moo-ui");
|
|
44
|
+
const input = document.getElementById(configuration.input);
|
|
45
|
+
const confirmation = configuration.confirmation && document.getElementById(configuration.confirmation);
|
|
46
|
+
const localControl = (node) => node?.matches('input[type="password"]') && node.closest(".moo-ui") === owner && node.form === element.closest("form");
|
|
47
|
+
const policy = configuration.policy;
|
|
48
|
+
if (!owner || !localControl(input) || configuration.confirmation && (!localControl(confirmation) || confirmation === input) || policy?.version !== 3 || !Number.isInteger(policy.minlength) || policy.minlength < 1 || !Number.isInteger(policy.maxlength) || policy.maxlength < policy.minlength || policy.maxlength > 128 || !Array.isArray(policy.rules) || !policy.rules.length || policy.rules.some((rule) => !["length", "case", "number", "symbol", "maximum"].includes(rule.key) || typeof rule.label !== "string" || rule.warning !== void 0 && typeof rule.warning !== "boolean") || new Set(policy.rules.map((rule) => rule.key)).size !== policy.rules.length || !["length", "maximum"].every((key) => policy.rules.some((rule) => rule.key === key)) || typeof policy.whitespace !== "string" || options.analyze !== void 0 && typeof options.analyze !== "function") {
|
|
49
|
+
throw new TypeError("PasswordRequirements requires same-owner controls and a versioned public policy.");
|
|
50
|
+
}
|
|
51
|
+
this._element = element;
|
|
52
|
+
this._initialReady = element.getAttribute("data-requirements-ready");
|
|
53
|
+
this._originals = /* @__PURE__ */ new Map();
|
|
54
|
+
this._listeners = [];
|
|
55
|
+
const local = (suffix) => {
|
|
56
|
+
const node = document.getElementById(element.id + "-" + suffix);
|
|
57
|
+
if (!node || !element.contains(node)) throw new TypeError("Missing local requirement description.");
|
|
58
|
+
this._originals.set(node, {
|
|
59
|
+
text: node.textContent,
|
|
60
|
+
className: node.className,
|
|
61
|
+
hidden: node.hidden,
|
|
62
|
+
met: node.getAttribute("data-requirement-met")
|
|
63
|
+
});
|
|
64
|
+
return node;
|
|
65
|
+
};
|
|
66
|
+
const rows = new Map(policy.rules.map((rule) => [rule.key, local(rule.key)]));
|
|
67
|
+
const summary = local("summary");
|
|
68
|
+
const match = confirmation && local("confirmation");
|
|
69
|
+
const hint = configuration.submit && document.getElementById(element.id + "-submit-hint");
|
|
70
|
+
const submit = configuration.submit && document.getElementById(configuration.submit);
|
|
71
|
+
if (configuration.submit && (!input.form || !hint?.matches('[data-bs-toggle="tooltip"]') || !element.contains(hint) || !submit?.matches('button[type="submit"], input[type="submit"]') || submit.closest(".moo-ui") !== owner || submit.form !== input.form)) {
|
|
72
|
+
throw new TypeError("PasswordRequirements requires a same-form submit Tooltip.");
|
|
73
|
+
}
|
|
74
|
+
const initialDisabled = submit?.disabled;
|
|
75
|
+
const initialTabIndex = hint?.getAttribute("tabindex");
|
|
76
|
+
const initialTitle = hint?.getAttribute("data-bs-title");
|
|
77
|
+
const initialRole = hint?.getAttribute("role");
|
|
78
|
+
const initialLabel = hint?.getAttribute("aria-label");
|
|
79
|
+
const initialHidden = hint?.hidden;
|
|
80
|
+
const restoreAttribute = (name, value) => {
|
|
81
|
+
if (value === null) hint.removeAttribute(name);
|
|
82
|
+
else hint.setAttribute(name, value);
|
|
83
|
+
};
|
|
84
|
+
const submitParent = submit?.parentNode;
|
|
85
|
+
const submitNext = submit?.nextSibling;
|
|
86
|
+
const hintParent = hint?.parentNode;
|
|
87
|
+
const hintNext = hint?.nextSibling;
|
|
88
|
+
const hintChildren = hint && [...hint.childNodes];
|
|
89
|
+
const view = document.defaultView;
|
|
90
|
+
const Tooltip = view.bootstrap?.Tooltip || view.Tooltip;
|
|
91
|
+
if (submit && (!Tooltip || typeof Tooltip.getInstance !== "function" || typeof Tooltip.getOrCreateInstance !== "function")) {
|
|
92
|
+
throw new TypeError("PasswordRequirements submit binding requires Bootstrap Tooltip.");
|
|
93
|
+
}
|
|
94
|
+
const previousTooltip = hint && Tooltip?.getInstance(hint);
|
|
95
|
+
const previousTooltipWasEnabled = previousTooltip?._isEnabled;
|
|
96
|
+
const previousTooltipContent = previousTooltip?._newContent;
|
|
97
|
+
let tooltip;
|
|
98
|
+
let gateReady = !submit;
|
|
99
|
+
let lastTitle;
|
|
100
|
+
if (submit) {
|
|
101
|
+
this._listeners.push(() => {
|
|
102
|
+
submit.disabled = initialDisabled;
|
|
103
|
+
if (initialTabIndex === null) hint.removeAttribute("tabindex");
|
|
104
|
+
else hint.setAttribute("tabindex", initialTabIndex);
|
|
105
|
+
restoreAttribute("data-bs-title", initialTitle);
|
|
106
|
+
restoreAttribute("role", initialRole);
|
|
107
|
+
restoreAttribute("aria-label", initialLabel);
|
|
108
|
+
tooltip?.hide();
|
|
109
|
+
if (!previousTooltip) tooltip?.dispose();
|
|
110
|
+
else {
|
|
111
|
+
tooltip.setContent(previousTooltipContent);
|
|
112
|
+
if (previousTooltipWasEnabled) tooltip.enable();
|
|
113
|
+
else tooltip.disable();
|
|
114
|
+
}
|
|
115
|
+
submitParent.insertBefore(submit, submitNext?.parentNode === submitParent ? submitNext : null);
|
|
116
|
+
hintParent.insertBefore(hint, hintNext?.parentNode === hintParent ? hintNext : null);
|
|
117
|
+
hint.replaceChildren(...hintChildren);
|
|
118
|
+
hint.hidden = initialHidden;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const updateSubmit = (states) => {
|
|
122
|
+
if (!submit) return;
|
|
123
|
+
gateReady = Boolean(input.value) && states.every((state) => state.met);
|
|
124
|
+
submit.disabled = !gateReady;
|
|
125
|
+
hint.tabIndex = gateReady ? -1 : 0;
|
|
126
|
+
restoreAttribute("role", gateReady ? null : initialRole);
|
|
127
|
+
restoreAttribute("aria-label", gateReady ? null : initialLabel);
|
|
128
|
+
const title = states.filter((state) => !state.met && (input.value || !state.warning || state.key === "confirmation")).map((state) => state.label).join(" ");
|
|
129
|
+
hint.setAttribute("data-bs-title", title || initialTitle);
|
|
130
|
+
if (gateReady) {
|
|
131
|
+
tooltip?.hide();
|
|
132
|
+
tooltip?.disable();
|
|
133
|
+
} else {
|
|
134
|
+
tooltip?.enable();
|
|
135
|
+
if (title !== lastTitle) tooltip?.setContent({ ".tooltip-inner": title || initialTitle });
|
|
136
|
+
}
|
|
137
|
+
lastTitle = title;
|
|
138
|
+
};
|
|
139
|
+
const show = (node, label, met, warning = false, active = Boolean(input.value)) => {
|
|
140
|
+
node.textContent = warning ? label : (met ? "\u2713 " : "\u2014 ") + label;
|
|
141
|
+
node.hidden = warning && (!active || met);
|
|
142
|
+
node.classList.toggle("text-body", !warning && met);
|
|
143
|
+
node.classList.toggle("text-danger", warning);
|
|
144
|
+
node.dataset.requirementMet = String(met);
|
|
145
|
+
};
|
|
146
|
+
const update = () => {
|
|
147
|
+
gateReady = !submit;
|
|
148
|
+
if (submit) submit.disabled = true;
|
|
149
|
+
const states = passwordRequirementStates(input.value, policy);
|
|
150
|
+
const withinMaximum = Math.max(
|
|
151
|
+
Array.from(input.value).length,
|
|
152
|
+
Array.from(input.value.normalize("NFKC")).length
|
|
153
|
+
) <= policy.maxlength;
|
|
154
|
+
let analysis = null;
|
|
155
|
+
const analysisValue = strip(input.value.normalize("NFKC"), policy.whitespace);
|
|
156
|
+
if (options.analyze && analysisValue && withinMaximum) {
|
|
157
|
+
analysis = options.analyze(analysisValue);
|
|
158
|
+
if (!analysis || typeof analysis.met !== "boolean" || !Number.isInteger(analysis.score) || analysis.score < 0 || analysis.score > 4) {
|
|
159
|
+
throw new TypeError("Password analysis must synchronously return {met: boolean, score: 0..4}.");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (options.analyze) states.push({
|
|
163
|
+
key: "analysis",
|
|
164
|
+
label: "Host password analysis must pass.",
|
|
165
|
+
met: Boolean(analysis?.met),
|
|
166
|
+
warning: true
|
|
167
|
+
});
|
|
168
|
+
if (confirmation) states.push({
|
|
169
|
+
key: "confirmation",
|
|
170
|
+
label: this._originals.get(match).text,
|
|
171
|
+
warning: true,
|
|
172
|
+
met: Boolean(confirmation.value) && confirmation.value === input.value
|
|
173
|
+
});
|
|
174
|
+
for (const state of states.filter((state2) => state2.key !== "analysis")) show(
|
|
175
|
+
state.key === "confirmation" ? match : rows.get(state.key),
|
|
176
|
+
state.label,
|
|
177
|
+
Boolean(input.value) && state.met,
|
|
178
|
+
state.warning,
|
|
179
|
+
state.key === "confirmation" ? Boolean(confirmation.value) : Boolean(input.value)
|
|
180
|
+
);
|
|
181
|
+
const missing = states.filter((state) => !state.met).length;
|
|
182
|
+
const warnings = states.filter((state) => state.warning && !state.met).map((state) => state.label).join(" ");
|
|
183
|
+
const summaryText = !input.value ? this._originals.get(summary).text : missing ? `Remaining: ${missing} ${warnings}`.trim() : "Shown checks met. Final validation happens on submit.";
|
|
184
|
+
if (summary.textContent !== summaryText) summary.textContent = summaryText;
|
|
185
|
+
options.onChange?.(states.map(({ key, met }) => ({ key, met: Boolean(input.value) && met })), Boolean(input.value), analysis?.score ?? null);
|
|
186
|
+
updateSubmit(states);
|
|
187
|
+
};
|
|
188
|
+
instances.set(element, this);
|
|
189
|
+
try {
|
|
190
|
+
if (submit) {
|
|
191
|
+
submitParent.insertBefore(hint, submit);
|
|
192
|
+
hint.replaceChildren(submit);
|
|
193
|
+
hint.hidden = false;
|
|
194
|
+
submit.disabled = true;
|
|
195
|
+
tooltip = Tooltip?.getOrCreateInstance(hint, { container: owner, html: false, trigger: "hover focus" });
|
|
196
|
+
}
|
|
197
|
+
for (const node of [input, confirmation].filter(Boolean)) {
|
|
198
|
+
for (const event of ["input", "change"]) {
|
|
199
|
+
node.addEventListener(event, update);
|
|
200
|
+
this._listeners.push(() => node.removeEventListener(event, update));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (input.form) {
|
|
204
|
+
if (submit) {
|
|
205
|
+
const guard = (event) => {
|
|
206
|
+
try {
|
|
207
|
+
update();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
event.preventDefault();
|
|
210
|
+
event.stopImmediatePropagation();
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
if (!gateReady) {
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
event.stopImmediatePropagation();
|
|
216
|
+
hint.focus();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
input.form.addEventListener("submit", guard, true);
|
|
220
|
+
this._listeners.push(() => input.form.removeEventListener("submit", guard, true));
|
|
221
|
+
}
|
|
222
|
+
const reset = () => {
|
|
223
|
+
clearTimeout(this._resetTimer);
|
|
224
|
+
this._resetTimer = setTimeout(update, 0);
|
|
225
|
+
};
|
|
226
|
+
input.form.addEventListener("reset", reset);
|
|
227
|
+
this._listeners.push(() => input.form.removeEventListener("reset", reset));
|
|
228
|
+
}
|
|
229
|
+
update();
|
|
230
|
+
element.dataset.requirementsReady = "true";
|
|
231
|
+
this.ready = Promise.resolve(this);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
this.dispose();
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
dispose() {
|
|
238
|
+
if (instances.get(this._element) !== this) return;
|
|
239
|
+
clearTimeout(this._resetTimer);
|
|
240
|
+
for (const remove of this._listeners.splice(0)) remove();
|
|
241
|
+
for (const [node, original] of this._originals) {
|
|
242
|
+
node.textContent = original.text;
|
|
243
|
+
node.className = original.className;
|
|
244
|
+
node.hidden = original.hidden;
|
|
245
|
+
if (original.met === null) node.removeAttribute("data-requirement-met");
|
|
246
|
+
else node.setAttribute("data-requirement-met", original.met);
|
|
247
|
+
}
|
|
248
|
+
if (this._initialReady === null) this._element.removeAttribute("data-requirements-ready");
|
|
249
|
+
else this._element.setAttribute("data-requirements-ready", this._initialReady);
|
|
250
|
+
instances.delete(this._element);
|
|
251
|
+
this._originals.clear();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
export {
|
|
255
|
+
PasswordRequirements as default,
|
|
256
|
+
passwordRequirementStates
|
|
257
|
+
};
|
package/dist/js/sheet.js
CHANGED
package/dist/js/sidebar.js
CHANGED
package/dist/js/slider.js
CHANGED
package/dist/js/state.js
CHANGED
package/dist/js/toc.js
CHANGED
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
{
|
|
4
4
|
"export": "./moo.css",
|
|
5
5
|
"path": "dist/assets/css/moo.css",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "d11bbace049bdcc51c609e7c3cfc255aa51cd8ae0ca67861abe632b5403d48d0"
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"export": "./moo-ui.css",
|
|
10
10
|
"path": "dist/assets/css/moo-ui.css",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "9cecb108344be63165a063e6a088657f7714ea7305aa0cc0e85e58474066f154"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"export": "./state.js",
|
|
15
15
|
"path": "dist/js/state.js",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "58307028fc5f06a83620a335f2de13fea3ad3959b6a9f9794d0d443b43b48acd"
|
|
17
17
|
}
|
|
18
18
|
],
|
|
19
19
|
"package": {
|
|
20
20
|
"name": "@wpmoo/ui",
|
|
21
|
-
"version": "
|
|
21
|
+
"version": "2.0.0"
|
|
22
22
|
},
|
|
23
23
|
"schemaVersion": 1
|
|
24
24
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wpmoo/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Bootstrap markup. shadcn feel. A frontend-framework-free component system for server-rendered apps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "WPMoo (https://wpmoo.org)",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"style": "dist/assets/css/moo-ui.css",
|
|
27
27
|
"exports": {
|
|
28
|
+
"./password-requirements.js": "./dist/js/password-requirements.js",
|
|
28
29
|
"./moo-ui.css": "./dist/assets/css/moo-ui.css",
|
|
29
30
|
"./moo-ui.min.css": "./dist/assets/css/moo-ui.min.css",
|
|
30
31
|
"./moo.css": "./dist/assets/css/moo.css",
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"./package.json": "./package.json"
|
|
57
58
|
},
|
|
58
59
|
"files": [
|
|
60
|
+
"dist/js/password-requirements.js",
|
|
59
61
|
"dist/assets/css/moo-ui.css",
|
|
60
62
|
"dist/assets/css/moo-ui.min.css",
|
|
61
63
|
"dist/assets/css/moo.css",
|
|
@@ -83,14 +85,18 @@
|
|
|
83
85
|
"certification.json",
|
|
84
86
|
"README.md",
|
|
85
87
|
"LICENSE",
|
|
86
|
-
"
|
|
88
|
+
"THIRD_PARTY_NOTICES.md"
|
|
87
89
|
],
|
|
88
90
|
"peerDependencies": {
|
|
89
|
-
"bootstrap": ">=5.3.0 <5.4"
|
|
91
|
+
"bootstrap": ">=5.3.0 <5.4",
|
|
92
|
+
"chart.js": ">=4.6.1 <5"
|
|
90
93
|
},
|
|
91
94
|
"peerDependenciesMeta": {
|
|
92
95
|
"bootstrap": {
|
|
93
96
|
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"chart.js": {
|
|
99
|
+
"optional": true
|
|
94
100
|
}
|
|
95
101
|
},
|
|
96
102
|
"sideEffects": [
|
|
@@ -109,7 +115,7 @@
|
|
|
109
115
|
},
|
|
110
116
|
"devDependencies": {
|
|
111
117
|
"axe-core": "4.14.0",
|
|
112
|
-
"chart.js": "4.
|
|
118
|
+
"chart.js": "4.6.1",
|
|
113
119
|
"esbuild": "0.28.2"
|
|
114
120
|
}
|
|
115
121
|
}
|
package/scss/mixins/_banner.scss
CHANGED
package/ASSET_LICENSE.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# WPMoo Visual Asset License
|
|
2
|
-
|
|
3
|
-
WPMoo-generated visual assets are copyright (c) 2026 WPMoo
|
|
4
|
-
(`wpmoo.org`). All rights reserved.
|
|
5
|
-
|
|
6
|
-
These assets are not licensed under the repository's MIT source code license
|
|
7
|
-
unless an individual file or directory states otherwise. This includes brand
|
|
8
|
-
assets, component preview images, marketing images, screenshots, generated
|
|
9
|
-
artwork, and other WPMoo-owned image assets under `site/static/images/`.
|
|
10
|
-
|
|
11
|
-
You may view these assets as part of the Moo UI documentation and demo site.
|
|
12
|
-
You may not copy, redistribute, modify, sell, sublicense, or use them in another
|
|
13
|
-
product, site, template, library, training dataset, or marketing asset without
|
|
14
|
-
prior written permission from WPMoo.
|
|
15
|
-
|
|
16
|
-
Third-party assets remain under their own licenses. See the
|
|
17
|
-
[version-pinned third-party notices](https://github.com/wpmoo-org/ui/blob/v1.5.0/THIRD_PARTY_NOTICES.md).
|
|
18
|
-
Those notices are repository-only and are intentionally not shipped in the npm
|
|
19
|
-
package.
|