@public-ui/components
Version:
Contains all web components that belong to KoliBri - The accessible HTML-Standard.
376 lines (375 loc) • 15.9 kB
JavaScript
/*!
* KoliBri - The accessible HTML-Standard
*/
import { getCssStyle, patchTheme, patchThemeTag } from "adopted-style-sheets";
import rgba from "rgba-convert";
import { hex, score } from "wcag-contrast";
import { getDocument, getExperimentalMode, Log } from "./dev.utils";
import { devHint } from "./a11y.tipps";
const OBJECT_OBJECT = /\[object Object\]/;
export const objectObjectHandler = (value, cb) => {
if (typeof value === 'string' && OBJECT_OBJECT.test(value)) {
return;
}
cb();
};
export const emptyStringByArrayHandler = (value, cb) => {
if (typeof value === 'string' && value === '') {
return;
}
cb();
};
export const setEventTarget = (event, target) => {
if (getExperimentalMode()) {
Log.debug([event, target]);
Log.debug(`↑ We propagate the (submit) event to this target.`);
}
Object.defineProperty(event, 'target', {
value: target,
writable: false,
});
};
const patchState = (component) => {
var _a, _b, _c;
(_a = component.nextHooks) === null || _a === void 0 ? void 0 : _a.forEach((hooks, key) => {
var _a;
const beforePatch = hooks.get('beforePatch');
if (typeof beforePatch === 'function') {
beforePatch((_a = component.nextState) === null || _a === void 0 ? void 0 : _a.get(key), component.nextState, component, key);
}
});
if (((_b = component.nextState) === null || _b === void 0 ? void 0 : _b.size) > 0) {
component.state = Object.assign(Object.assign({}, component.state), Object.fromEntries(component.nextState));
delete component.nextState;
(_c = component.nextHooks) === null || _c === void 0 ? void 0 : _c.forEach((hooks, key) => {
const afterPatch = hooks.get('afterPatch');
if (typeof afterPatch === 'function') {
afterPatch(component.state[key], component.state, component, key);
}
});
}
delete component.nextHooks;
};
export const setState = (component, propName, value, hooks = {}) => {
var _a, _b;
if (component.nextHooks === undefined) {
component.nextHooks = new Map();
}
if (component.nextState === undefined) {
component.nextState = new Map();
}
const nextHooks = component.nextHooks.get(propName);
if (nextHooks instanceof Map === false) {
component.nextHooks.set(propName, new Map());
}
if (typeof hooks.afterPatch === 'function') {
(_a = component.nextHooks.get(propName)) === null || _a === void 0 ? void 0 : _a.set('afterPatch', hooks.afterPatch);
}
if (typeof hooks.beforePatch === 'function') {
(_b = component.nextHooks.get(propName)) === null || _b === void 0 ? void 0 : _b.set('beforePatch', hooks.beforePatch);
}
component.nextState.set(propName, value);
patchState(component);
};
const logWarn = (component, propName, value, requiredGeneric) => {
devHint(`[${component.constructor.name}] The property value: (${value}) for '${propName}' is not valid. Allowed values are: ${Array.from(requiredGeneric).join(', ')}`);
};
export function watchValidator(component, propName, validationFunction, requiredGeneric, value, options = {}) {
if (validationFunction(value)) {
setState(component, propName, value, options.hooks);
}
else if (value === undefined && options.required !== true && validationFunction(options.defaultValue)) {
setState(component, propName, options.defaultValue, options.hooks);
}
else {
if (!options.required) {
requiredGeneric.add(null);
}
logWarn(component, propName, value, requiredGeneric);
}
}
export const watchBoolean = (component, propName, value, options) => {
watchValidator(component, propName, (value) => typeof value === 'boolean', new Set(['Boolean {true, false}']), value, options);
};
export const watchString = (component, propName, value, options = {}) => {
const minLength = typeof options.minLength === 'number' ? options === null || options === void 0 ? void 0 : options.minLength : 0;
watchValidator(component, propName, (value) => typeof value === 'string' && value.length >= minLength && (typeof (options === null || options === void 0 ? void 0 : options.maxLength) === 'undefined' || value.length <= options.maxLength), new Set([`String`]), value, options);
};
export const watchNumber = (component, propName, value, options) => {
watchValidator(component, propName, (value) => typeof value === 'number' &&
(typeof (options === null || options === void 0 ? void 0 : options.min) === 'undefined' || (typeof (options === null || options === void 0 ? void 0 : options.min) === 'number' && value >= options.min)) &&
(typeof (options === null || options === void 0 ? void 0 : options.max) === 'undefined' || (typeof (options === null || options === void 0 ? void 0 : options.max) === 'number' && value <= options.max)), new Set(['Number']), value, options);
};
export const watchJsonArrayString = (component, propName, itemValidation, value, arrayValidation = (items) => items === items, options = {}) => {
emptyStringByArrayHandler(value, () => {
objectObjectHandler(value, () => {
if (typeof value === 'undefined') {
value = [];
}
try {
try {
value = parseJson(value);
}
catch (_a) {
}
if (Array.isArray(value)) {
const invalid = value.find((item) => !itemValidation(item));
if (invalid === undefined && arrayValidation(value)) {
setState(component, propName, value, options.hooks);
}
else {
objectObjectHandler(invalid, () => {
Log.debug(invalid);
throw new Error(`↑ The schema for the property (_options) is not valid. The value will not be changed.`);
});
}
}
else {
objectObjectHandler(value, () => {
Log.debug(value);
throw new Error(`↑ The schema for the property (_options) is not valid. The value will not be changed.`);
});
}
}
catch (error) {
Log.debug(error);
}
});
});
};
export const stringifyJson = (value) => {
try {
return JSON.stringify(value).replace(/"/g, "'");
}
catch (_a) {
Log.warn(['stringifyJson', value]);
Log.error(`↑ The JSON could not be converted to a string. A stringifiable JSON is expected.`);
throw new Error();
}
};
const JSON_CHARS = /^[{[]/;
export const parseJson = (value) => {
if (typeof value === 'string') {
try {
return JSON.parse(value);
}
catch (_a) {
if (JSON_CHARS.test(value)) {
try {
return JSON.parse(value.replace(/'/g, '"'));
}
catch (_b) {
Log.warn(['parseJson', value]);
Log.error(`↑ The JSON string could not be parsed. Make sure that single quotes in the text are escaped (‘).`);
}
}
}
}
throw new Error();
};
export const mapBoolean2String = (value) => {
return typeof value === 'boolean' ? (value === true ? 'true' : 'false') : undefined;
};
export const mapStringOrBoolean2String = (value) => {
return typeof value === 'string' ? value : mapBoolean2String(value);
};
const querySelectorShadow = (selector, rootNode) => {
const visited = new Set();
const queue = [rootNode];
let index = 0;
while (index < queue.length) {
const current = queue[index++];
if (visited.has(current)) {
continue;
}
visited.add(current);
if (current instanceof Element && current.matches(selector)) {
return current;
}
const children = Array.from(current.children || []);
for (let i = 0; i < children.length; i++) {
queue.push(children[i]);
}
if (current instanceof HTMLElement && current.shadowRoot) {
queue.push(current.shadowRoot);
}
if (current instanceof HTMLSlotElement) {
const assigned = current.assignedNodes({ flatten: true });
for (let i = 0; i < assigned.length; i++) {
if (assigned[i] instanceof Element) {
queue.push(assigned[i]);
}
}
}
}
return null;
};
const querySelectorAllShadow = (selector, rootNode) => {
const visited = new Set();
const results = [];
const resultSet = new Set();
const queue = [rootNode];
let index = 0;
while (index < queue.length) {
const current = queue[index++];
if (visited.has(current)) {
continue;
}
visited.add(current);
if (current instanceof Element && current.matches(selector) && !resultSet.has(current)) {
results.push(current);
resultSet.add(current);
}
const children = Array.from(current.children || []);
for (let i = 0; i < children.length; i++) {
queue.push(children[i]);
}
if (current instanceof HTMLElement && current.shadowRoot) {
queue.push(current.shadowRoot);
}
if (current instanceof HTMLSlotElement) {
const assigned = current.assignedNodes({ flatten: true });
for (let i = 0; i < assigned.length; i++) {
if (assigned[i] instanceof Element) {
queue.push(assigned[i]);
}
}
}
}
return results;
};
export const koliBriQuerySelector = (selector, node) => querySelectorShadow(selector, node || getDocument());
export const koliBriQuerySelectorAll = (selector, node) => querySelectorAllShadow(selector, node || getDocument());
let DEFAULT_COLOR_CONTRAST = null;
const getDefaultColorContrast = () => {
DEFAULT_COLOR_CONTRAST = DEFAULT_COLOR_CONTRAST || {
backgroundColor: '#00000000',
color: '#00000000',
domNode: getDocument().body,
level: 'Fail',
score: 1,
};
return DEFAULT_COLOR_CONTRAST;
};
const TRANSPARENT_REGEXP = /(\d+, ){3}0\)/;
export const koliBriA11yColorContrast = (domNode, a11yColorContrast = getDefaultColorContrast()) => {
const computedStyle = getComputedStyle(domNode);
const hexBG = TRANSPARENT_REGEXP.test(computedStyle.backgroundColor) ? a11yColorContrast.backgroundColor : rgba.hex(computedStyle.backgroundColor);
const hexC = TRANSPARENT_REGEXP.test(computedStyle.color) ? a11yColorContrast.color : rgba.hex(computedStyle.color);
const diff = hex(hexBG, hexC);
const contrast = {
backgroundColor: hexBG,
color: hexC,
domNode: domNode,
level: score(diff),
score: diff,
};
if (diff < 4.5) {
Log.error([
'Color-Contrast-Error',
{
backgroundColor: contrast.backgroundColor,
color: contrast.color,
level: contrast.level,
score: contrast.score,
},
contrast.domNode,
]);
}
return contrast;
};
export const koliBriQuerySelectorColors = (selector, a11yColorContrast = getDefaultColorContrast()) => {
if (a11yColorContrast.domNode instanceof HTMLElement) {
a11yColorContrast = koliBriA11yColorContrast(a11yColorContrast.domNode, a11yColorContrast);
}
const selectedNode = a11yColorContrast.domNode.querySelector(selector);
if (selectedNode === null) {
const nodeList = a11yColorContrast.domNode.querySelectorAll('[class="hydrated"]');
for (let i = 0; i < nodeList.length; i++) {
a11yColorContrast.domNode = nodeList[i];
a11yColorContrast = koliBriQuerySelectorColors(selector, a11yColorContrast);
if (a11yColorContrast.domNode !== null) {
break;
}
}
return a11yColorContrast;
}
else {
return koliBriA11yColorContrast(selectedNode, a11yColorContrast);
}
};
export class KoliBriUtils {
static queryHtmlElementColors(targetNode, a11yColorContrast, recursion = false, log = true) {
let returnValue = null;
if (recursion === true || KoliBriUtils.executionLock === false) {
if (recursion === false) {
KoliBriUtils.cache.clear();
KoliBriUtils.cache.set(a11yColorContrast.domNode, a11yColorContrast);
KoliBriUtils.executionLock = true;
if (log === true) {
Log.debug(`[KoliBriUtils] Color contrast analysis started...`);
}
}
if (targetNode === a11yColorContrast.domNode) {
returnValue = a11yColorContrast;
}
else {
const children = new Set();
if (a11yColorContrast.domNode.shadowRoot) {
const shadowChildren = a11yColorContrast.domNode.shadowRoot.children;
for (let i = 0; i < shadowChildren.length; i++) {
children.add(shadowChildren[i]);
}
}
const slotElement = a11yColorContrast.domNode;
if (typeof slotElement.assignedNodes === 'function') {
const slotChildren = slotElement.assignedNodes();
for (let i = 0; i < slotChildren.length; i++) {
if (slotChildren[i] instanceof HTMLElement) {
children.add(slotChildren[i]);
}
}
}
const domChildren = a11yColorContrast.domNode.children;
for (let i = 0; i < domChildren.length; i++) {
children.add(domChildren[i]);
}
const arrayChildren = Array.from(children);
for (let i = 0; i < arrayChildren.length; i++) {
let colorContrast = KoliBriUtils.cache.get(arrayChildren[i]);
if (colorContrast === undefined) {
colorContrast = koliBriA11yColorContrast(arrayChildren[i], a11yColorContrast);
}
KoliBriUtils.cache.set(arrayChildren[i], colorContrast);
const colors = KoliBriUtils.queryHtmlElementColors(targetNode, colorContrast, true, false);
if (colors !== null) {
returnValue = colors;
break;
}
}
}
}
else {
Log.debug(`[KoliBriUtils] Call aborted because a color contrast analysis is currently being executed.`);
}
if (recursion === false) {
if (log === true) {
Log.debug(`[KoliBriUtils] Color contrast analysis finished (${KoliBriUtils.cache.size} DOM elements are analysed).`);
}
KoliBriUtils.executionLock = false;
KoliBriUtils.cache.clear();
}
return returnValue;
}
}
KoliBriUtils.executionLock = false;
KoliBriUtils.cache = new Map();
export class KoliBriDevHelper {
}
KoliBriDevHelper.getCssStyle = getCssStyle;
KoliBriDevHelper.patchTheme = patchTheme;
KoliBriDevHelper.patchThemeTag = patchThemeTag;
KoliBriDevHelper.querySelector = koliBriQuerySelector;
KoliBriDevHelper.querySelectorAll = koliBriQuerySelectorAll;
KoliBriDevHelper.stringifyJson = stringifyJson;
//# sourceMappingURL=prop.validators.js.map