mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 12:09:26 +00:00
* Basic scaffolding * Generate theme & palette data.js that other JS can import * Make it possible to include page-card without links * WIP * Add `appearance` to details, closes #569 Except `accent` as that's a) far less useful and b) trickier due to the icon color * Fix broken link * WIP * WIP * Icons icon * Unify styles for interactive cards * Prevent focusing inside theme icons * Fixes * Action page cards * Panel scrollables * scrollable * Scroll shadows * Add renaming UI * UI * Move styling of heading icons to `ui.css` * Support permalinks & CRUD * Make clickable cards more accessible * Style cards a little better * Default to styles panel if theme is selected * Update theme-icons.css * Custom themes should be saved under Custom * Get theme code * Bigger title * Fixes * Use theme Vue app for remixing too * Fix preview jank and make preview script more flexible * Make radio groups scrollable * Add affordance to button cards * Sticky * `<color-select>` * Fix theme remixing * Improve previewing logic * Fix preview * Move `domChange()` to separate module `theme-picker.js` includes side-effects, which may not always be desirable everywhere we may want to import `domChange()` * Update preview.js * Panel animation * Hide Save button if no changes and not saved * Do not show blank code when no selection has been made * Use theme slug in filename * Remove unused component * Better UI for editing title (and any other text) * Tweak UI of renaming * Better indicate default selection * Fix preview reverting bug * Fill out app preview with more examples * Remove `zoom` from theme showcase (yields unexpected/painful results Safari), improve display in wider viewports * Pending delete * Make styles panel cards scrollable * Fix some of the Safari issues * Update search.css * Update panel.css * Select preview UI * Fix typo * Frame colors setting as color contrast * Show dark mode in color mappings * Brand color * Swatch styling * Fix caret icon * Move Starting theme to the same level as other controls * Rename typography to Fonts * Fix bug: Swatch select should show swatches from the selected palette * Move capitalize to shared utils * Add utils for handling nested objects * Icons panel * Update code.js * Move utils around * Add fit and finish to sidebar panels * Theme card: Move icons to separate data structure * Move data to dedicated folder since we now have a lot more of it * Add default icon families and variants to themes * Data * Add `deepEntries()` * Add Duotone * Spruce up icons preview * Use theme's icon family in showcase * Font cards * Font cards * Add `max-inline-size` to preview container * Remove alternate preview options * Remove theme subtitle * Support FA kit codes * Remove Pro badges from theme cards * Use panagram preview for Fonts * Consistent heading and label capitalization * Classes for different icons-card types * Update data.js.njk * Variable style on icon family cards * Fix Sharp Duotone * Clean up FA kit code hint * Hide non-functional Icon Library field * Fix theme icon heights * icon variant -> style in theme metadata * Fix bug with icons defaults not being shown * More convenient theme defaults * Fix bug with non updating URL * Fix bug * Fix multiplying badges * Custom docs pages * Add Duotone icons to Mellow theme * Fix 404 * Remove "Create" from sidebar * Fix bug * Move vue components to `/assets/`, move their CSS with them * Safari/FF compatibility * Make panels scrollable again * Fix extra spacing --------- Co-authored-by: lindsaym-fa <dev@lindsaym.design>
181 lines
4.4 KiB
JavaScript
181 lines
4.4 KiB
JavaScript
/**
|
|
* @typedef { string | number | Symbol } Property
|
|
* @typedef { (value: any, key: Property, parent: object, path: Property[]) => any } EachCallback
|
|
*/
|
|
|
|
export function isPlainObject(obj) {
|
|
return isObject(obj, 'Object');
|
|
}
|
|
|
|
export function isObject(obj, type) {
|
|
if (!obj || typeof obj !== 'object') {
|
|
return false;
|
|
}
|
|
|
|
let proto = Object.getPrototypeOf(obj);
|
|
return proto.constructor?.name === type;
|
|
}
|
|
|
|
export function deepMerge(target, source, options = {}) {
|
|
let {
|
|
emptyValues = [undefined],
|
|
containers = ['Object', 'EventTarget'],
|
|
isContainer = value => containers.some(type => isObject(value, type)),
|
|
} = options;
|
|
|
|
if (isContainer(target) && isContainer(source)) {
|
|
for (let key in source) {
|
|
if (key in target && isContainer(target[key]) && isContainer(source[key])) {
|
|
target[key] = deepMerge(target[key], source[key], options);
|
|
} else if (!emptyValues.includes(source[key])) {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
return target ?? source;
|
|
}
|
|
|
|
/**
|
|
* Iterate over a deep array, recursively for plain objects
|
|
* @param { any } obj The object to iterate over. Can be an array or a plain object, or even a primitive value.
|
|
* @param { EachCallback } callback. value is === parent[key]
|
|
* @param { object } [parentObj] The parent object of the current value Mainly used internally to facilitate recursion.
|
|
* @param { Property } [key] The key of the current value. Mainly used internally to facilitate recursion.
|
|
* @param { Property[] } [path] Any existing path (not including the key). Mainly used internally to facilitate recursion.
|
|
*/
|
|
export function deepEach(obj, callback, parentObj, key, path = []) {
|
|
if (key !== undefined) {
|
|
let ret = callback(obj, key, parentObj, path);
|
|
|
|
if (ret !== undefined) {
|
|
if (ret === false) {
|
|
// Do not descend further
|
|
return;
|
|
}
|
|
|
|
// Overwrite value
|
|
parentObj[key] = ret;
|
|
obj = ret;
|
|
}
|
|
}
|
|
|
|
let newPath = key !== undefined ? [...path, key] : path;
|
|
|
|
if (Array.isArray(obj)) {
|
|
for (let i = 0; i < obj.length; i++) {
|
|
deepEach(obj[i], callback, obj, i, newPath);
|
|
}
|
|
} else if (isPlainObject(obj)) {
|
|
for (let key in obj) {
|
|
deepEach(obj[key], callback, obj, key, newPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a value from a deeply nested object
|
|
* @param {*} obj
|
|
* @param {PropertyPath} path
|
|
* @returns
|
|
*/
|
|
export function deepGet(obj, path) {
|
|
if (path.length === 0) {
|
|
return obj;
|
|
}
|
|
|
|
let ret = obj;
|
|
|
|
for (let key of path) {
|
|
if (ret === undefined) {
|
|
return undefined;
|
|
}
|
|
|
|
ret = ret[key];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Set a value in a deep object, creating object literals as needed
|
|
* @param { * } obj
|
|
* @param { Property[] } path
|
|
* @param { any } value
|
|
*/
|
|
export function deepSet(obj, path, value) {
|
|
if (path.length === 0) {
|
|
return;
|
|
}
|
|
|
|
let key = path.pop();
|
|
|
|
let ret = path.reduce((acc, property) => {
|
|
if (acc[property] === undefined) {
|
|
acc[property] = {};
|
|
}
|
|
|
|
return acc[property];
|
|
}, obj);
|
|
|
|
ret[key] = value;
|
|
}
|
|
|
|
export function deepClone(obj) {
|
|
if (!obj) {
|
|
return obj;
|
|
}
|
|
|
|
let ret = obj;
|
|
|
|
if (Array.isArray(obj)) {
|
|
ret = obj.map(item => deepClone(item));
|
|
} else if (isPlainObject(obj)) {
|
|
ret = { ...obj };
|
|
|
|
for (let key in obj) {
|
|
ret[key] = deepClone(obj[key]);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Like Object.entries, but for deeply nested objects.
|
|
* For shallow objects the output is the same as Object.entries.
|
|
* @param {*} obj
|
|
* @param { object } options
|
|
* @param { EachCallback } each - If this returns false, the entry is not added to the result and the recursion is stopped.
|
|
* @param { EachCallback } filter - If this returns false, the entry is not added to the result.
|
|
* @param { EachCallback } descend - If this returns false, recursion is stopped.
|
|
* @returns {any[][]}
|
|
*/
|
|
export function deepEntries(obj, options = {}) {
|
|
let { each, filter, descend } = options;
|
|
let entries = [];
|
|
|
|
deepEach(obj, (value, key, parent, path) => {
|
|
let ret = each?.(value, key, parent, path);
|
|
|
|
if (ret !== false) {
|
|
let included = filter?.(value, key, parent, path) ?? true;
|
|
|
|
if (included) {
|
|
entries.push([...path, key, value]);
|
|
}
|
|
|
|
let descendRet = descend?.(value, key, parent, path);
|
|
if (descendRet === false) {
|
|
return false; // Stop recursion
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
});
|
|
|
|
return entries;
|
|
}
|