mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 12:09:26 +00:00
Changes
- Signature - `getKey` from URL - Fix bugs with `deepEntries()` / `flatten()`
This commit is contained in:
@@ -196,7 +196,8 @@ Here's an example that registers an icon library located in the `/assets/icons`
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('my-icons', {
|
||||
registerIconLibrary({
|
||||
name: 'my-icons',
|
||||
resolver: (name, family, variant) => `/assets/icons/${name}.svg`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
@@ -224,7 +225,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/tw
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: (name, family) => {
|
||||
const suffix = family === 'filled' ? '-fill' : '';
|
||||
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
|
||||
@@ -243,7 +245,8 @@ Icons in this library are licensed under the [Creative Commons 4.0 License](http
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('boxicons', {
|
||||
registerIconLibrary({
|
||||
name: 'boxicons',
|
||||
resolver: name => {
|
||||
let folder = 'regular';
|
||||
if (name.substring(0, 4) === 'bxs-') folder = 'solid';
|
||||
@@ -297,7 +300,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/lu
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('lucide', {
|
||||
registerIconLibrary({
|
||||
name: 'lucide',
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/lucide-static@0.16.29/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
@@ -313,7 +317,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/ta
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('heroicons', {
|
||||
registerIconLibrary({
|
||||
name: 'heroicons',
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/heroicons@2.0.1/24/outline/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
@@ -338,7 +343,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/lu
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('iconoir', {
|
||||
registerIconLibrary({
|
||||
name: 'iconoir',
|
||||
resolver: name => `https://cdn.jsdelivr.net/gh/lucaburgio/iconoir@latest/icons/${name}.svg`
|
||||
});
|
||||
</script>
|
||||
@@ -363,7 +369,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/io
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('ionicons', {
|
||||
registerIconLibrary({
|
||||
name: 'ionicons',
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/ionicons@5.1.2/dist/ionicons/svg/${name}.svg`,
|
||||
mutator: svg => {
|
||||
svg.setAttribute('fill', 'currentColor');
|
||||
@@ -408,7 +415,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/mi
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('jam', {
|
||||
registerIconLibrary({
|
||||
name: 'jam',
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/jam-icons@2.0.0/svg/${name}.svg`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor')
|
||||
});
|
||||
@@ -441,7 +449,8 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('material', {
|
||||
registerIconLibrary({
|
||||
name: 'material',
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)(_(round|sharp))?$/);
|
||||
return `https://cdn.jsdelivr.net/npm/@material-icons/svg@1.0.5/svg/${match[1]}/${match[3] || 'outline'}.svg`;
|
||||
@@ -484,7 +493,8 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('remixicon', {
|
||||
registerIconLibrary({
|
||||
name: 'remixicon',
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)\/(.*?)?$/);
|
||||
match[1] = match[1].charAt(0).toUpperCase() + match[1].slice(1);
|
||||
@@ -521,7 +531,8 @@ Icons in this library are licensed under the [MIT License](https://github.com/ta
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('tabler', {
|
||||
registerIconLibrary({
|
||||
name: 'tabler',
|
||||
resolver: name => `https://cdn.jsdelivr.net/npm/@tabler/icons@1.68.0/icons/${name}.svg`,
|
||||
mutator: svg => {
|
||||
svg.style.fill = 'none';
|
||||
@@ -557,7 +568,8 @@ Icons in this library are licensed under the [Apache 2.0 License](https://github
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('unicons', {
|
||||
registerIconLibrary({
|
||||
name: 'unicons',
|
||||
resolver: name => {
|
||||
const match = name.match(/^(.*?)(-s)?$/);
|
||||
return `https://cdn.jsdelivr.net/npm/@iconscout/unicons@3.0.3/svg/${match[2] === '-s' ? 'solid' : 'line'}/${
|
||||
@@ -595,7 +607,8 @@ For example, this will change the default icon library to use [Bootstrap Icons](
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: (name, family) => {
|
||||
const suffix = family === 'filled' ? '-fill' : '';
|
||||
return `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/icons/${name}${suffix}.svg`
|
||||
@@ -620,7 +633,8 @@ For security reasons, browsers may apply the same-origin policy on `<use>` eleme
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('sprite', {
|
||||
registerIconLibrary({
|
||||
name: 'sprite',
|
||||
resolver: name => `/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true
|
||||
@@ -642,7 +656,8 @@ This creates a mapping of `(name, library, variant)` to SVG markup:
|
||||
<script type="module">
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: name => `/path/to/custom/icons/${name}.svg`,
|
||||
fetched: {
|
||||
check: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>',
|
||||
|
||||
@@ -1412,7 +1412,8 @@ hasOutline: false
|
||||
import { registerIconLibrary } from '/dist/webawesome.js';
|
||||
|
||||
// Ensure regular icons are always available for the knobs
|
||||
registerIconLibrary('fa-classic-regular', {
|
||||
registerIconLibrary({
|
||||
name: 'fa-classic-regular',
|
||||
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/regular/${name}.svg`
|
||||
});
|
||||
|
||||
@@ -1453,36 +1454,47 @@ hasOutline: false
|
||||
break;
|
||||
case 'premium':
|
||||
iconFamily.value = 'custom';
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: name => `/assets/icons/chunk/${name}.svg`,
|
||||
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
|
||||
});
|
||||
registerIconLibrary('system', {
|
||||
registerIconLibrary({
|
||||
name: 'system',
|
||||
resolver: name => `/assets/icons/chunk/${name}.svg`,
|
||||
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
|
||||
});
|
||||
break;
|
||||
case 'playful':
|
||||
iconFamily.value = 'custom';
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: name => `/assets/icons/jelly/${name}.svg`,
|
||||
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
|
||||
});
|
||||
registerIconLibrary('system', {
|
||||
registerIconLibrary({
|
||||
name: 'system',
|
||||
name: 'system',
|
||||
name: 'system',
|
||||
resolver: name => `/assets/icons/jelly/${name}.svg`,
|
||||
mutator: svg => {[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));}
|
||||
});
|
||||
break;
|
||||
case 'brutalist':
|
||||
caregisterIconLibrary({
|
||||
registerIconLibrary({
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
iconFamily.value = 'custom';
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: name => `/assets/icons/utility/${name}.svg`,
|
||||
mutator: svg => {
|
||||
[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));
|
||||
[...svg.querySelectorAll('[stroke="black"]')].map(el => el.setAttribute('stroke', 'currentColor'));
|
||||
}
|
||||
});
|
||||
registerIconLibrary('system', {
|
||||
registerIconLibrary({
|
||||
name: 'system',
|
||||
resolver: name => `/assets/icons/utility/${name}.svg`,
|
||||
mutator: svg => {
|
||||
[...svg.querySelectorAll('[fill="black"]')].map(el => el.setAttribute('fill', 'currentColor'));
|
||||
@@ -1497,10 +1509,12 @@ hasOutline: false
|
||||
break;
|
||||
case 'classic':
|
||||
iconFamily.value = 'custom';
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: name => `/assets/icons/bootstrap/${name}.svg`,
|
||||
});
|
||||
registerIconLibrary('system', {
|
||||
registerIconLibrary({
|
||||
name: 'system',
|
||||
resolver: name => `/assets/icons/bootstrap/${name}.svg`,
|
||||
});
|
||||
break;
|
||||
@@ -1531,7 +1545,8 @@ hasOutline: false
|
||||
iconLibrary = 'sharp-solid';
|
||||
}
|
||||
// Ensures sharp-solid variations are available for ratings, etc.
|
||||
registerIconLibrary('always-solid', {
|
||||
registerIconLibrary({
|
||||
name: 'always-solid',
|
||||
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/sharp-solid/${name}.svg`
|
||||
});
|
||||
solidifyRatingStars();
|
||||
@@ -1557,15 +1572,18 @@ hasOutline: false
|
||||
iconLibrary = 'solid';
|
||||
}
|
||||
// Ensures solid variations are available for radios, ratings, etc.
|
||||
registerIconLibrary('always-solid', {
|
||||
registerIconLibrary({
|
||||
name: 'always-solid',
|
||||
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/solid/${name}.svg`
|
||||
});
|
||||
solidifyRatingStars();
|
||||
}
|
||||
registerIconLibrary('default', {
|
||||
registerIconLibrary({
|
||||
name: 'default',
|
||||
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/${iconLibrary}/${name}.svg`
|
||||
});
|
||||
registerIconLibrary('system', {
|
||||
registerIconLibrary({
|
||||
name: 'system',
|
||||
resolver: name => `https://ka-f.fontawesome.com/releases/v6.5.1/svgs/${iconLibrary}/${name}.svg`
|
||||
});
|
||||
};
|
||||
|
||||
@@ -24,7 +24,8 @@ const testLibraryIcons = {
|
||||
|
||||
describe('<wa-icon>', () => {
|
||||
before(() => {
|
||||
registerIconLibrary('test-library', {
|
||||
registerIconLibrary({
|
||||
name: 'test-library',
|
||||
resolver: (name: keyof typeof testLibraryIcons) => {
|
||||
// only for testing a bad request
|
||||
if (name === ('bad-request' as keyof typeof testLibraryIcons)) {
|
||||
@@ -170,7 +171,8 @@ describe('<wa-icon>', () => {
|
||||
describe('svg sprite sheets', () => {
|
||||
// TODO: this test is skipped because Bootstrap sprite.svg doesn't seem to be available in CI. Will fix in a future PR. [Konnor]
|
||||
it.skip('Should properly grab an SVG and render it from bootstrap icons', async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
registerIconLibrary({
|
||||
name: 'sprite',
|
||||
resolver: name => `/docs/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true,
|
||||
@@ -197,7 +199,8 @@ describe('<wa-icon>', () => {
|
||||
});
|
||||
|
||||
it('Should render nothing if the sprite hash is wrong', async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
registerIconLibrary({
|
||||
name: 'sprite',
|
||||
resolver: name => `/docs/assets/images/sprite.svg#${name}`,
|
||||
mutator: svg => svg.setAttribute('fill', 'currentColor'),
|
||||
spriteSheet: true,
|
||||
@@ -224,7 +227,8 @@ describe('<wa-icon>', () => {
|
||||
// TODO: <use> svg icons don't emit a "load" or "error" event...if we can figure out how to get the event to emit errors.
|
||||
// Once we figure out how to emit errors / loading perhaps we can actually test this?
|
||||
it.skip("Should produce an error if the icon doesn't exist.", async () => {
|
||||
registerIconLibrary('sprite', {
|
||||
registerIconLibrary({
|
||||
name: 'sprite',
|
||||
resolver(name) {
|
||||
return `/docs/assets/images/sprite.svg#${name}`;
|
||||
},
|
||||
|
||||
@@ -136,15 +136,22 @@ export default class WaIcon extends WebAwesomeElement {
|
||||
return this.svg;
|
||||
}
|
||||
|
||||
let markup: string;
|
||||
let cacheKey = library?.getKey?.(url) ?? url;
|
||||
let markup: string | undefined = library?.fetched?.[cacheKey];
|
||||
|
||||
if (library?.fetched?.[url]) {
|
||||
markup = library.fetched[url] as string;
|
||||
} else {
|
||||
if (!markup) {
|
||||
try {
|
||||
fileData = await fetch(url, { mode: 'cors' });
|
||||
if (!fileData.ok) return fileData.status === 410 ? CACHEABLE_ERROR : RETRYABLE_ERROR;
|
||||
markup = await fileData.text();
|
||||
|
||||
if (fileData.ok) {
|
||||
markup = await fileData.text();
|
||||
|
||||
if (library) {
|
||||
library.fetched![cacheKey] = markup;
|
||||
}
|
||||
} else {
|
||||
return fileData.status === 410 ? CACHEABLE_ERROR : RETRYABLE_ERROR;
|
||||
}
|
||||
} catch {
|
||||
return RETRYABLE_ERROR;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getKitCode } from '../../utilities/base-path.js';
|
||||
import type { IconLibrary, IconLibraryCache } from './types.d.ts';
|
||||
import type { IconLibraryCache, UnregisteredIconLibrary } from './types.d.ts';
|
||||
|
||||
function getIconUrl(name: string, family: string, variant: string) {
|
||||
const kitCode = getKitCode();
|
||||
@@ -66,12 +66,10 @@ export const fetched: IconLibraryCache<2> = {
|
||||
},
|
||||
};
|
||||
|
||||
const library: IconLibrary = {
|
||||
const library: UnregisteredIconLibrary = {
|
||||
name: 'default',
|
||||
resolver: (name: string, family = 'classic', variant = 'solid') => {
|
||||
return getIconUrl(name, family, variant);
|
||||
},
|
||||
// @ts-expect-error
|
||||
resolver: getIconUrl,
|
||||
getKey: url => url.replace(/\?token=[^&]+/, ''),
|
||||
fetched,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export type { IconLibrary, IconLibraryCache, IconLibraryFetched, UnregisteredIco
|
||||
let registry: IconLibrary[] = [];
|
||||
let watchedIcons: WaIcon[] = [];
|
||||
|
||||
registerIconLibrary('default', defaultLibrary);
|
||||
registerIconLibrary(defaultLibrary);
|
||||
|
||||
/** Adds an icon to the list of watched icons. */
|
||||
export function watchIcon(icon: WaIcon) {
|
||||
@@ -32,26 +32,26 @@ export function getIconLibrary(name?: string) {
|
||||
}
|
||||
|
||||
/** Adds an icon library to the registry, or overrides an existing one. */
|
||||
export function registerIconLibrary(name: string, options: UnregisteredIconLibrary) {
|
||||
unregisterIconLibrary(name);
|
||||
export function registerIconLibrary(library: UnregisteredIconLibrary) {
|
||||
unregisterIconLibrary(library.name);
|
||||
|
||||
registry.push({
|
||||
name,
|
||||
resolver: options.resolver,
|
||||
mutator: options.mutator,
|
||||
spriteSheet: options.spriteSheet,
|
||||
fetched: options.fetched ? flattenIconLibraryCache(options.fetched, options.resolver) : {},
|
||||
let registeredLibrary: IconLibrary = {
|
||||
...library,
|
||||
fetched: {},
|
||||
addFetched(cache: IconLibraryFetched) {
|
||||
// Convert flat URL → markup cache to deep family → variant → icon name → markup cache
|
||||
let flatCache = flattenIconLibraryCache(cache, this.resolver);
|
||||
this.fetched ??= {};
|
||||
Object.assign(this.fetched, flatCache);
|
||||
return addFetched.call(this, cache);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (library.fetched) {
|
||||
registeredLibrary.addFetched(library.fetched);
|
||||
}
|
||||
|
||||
registry.push(registeredLibrary);
|
||||
|
||||
// Redraw watched icons
|
||||
watchedIcons.forEach(icon => {
|
||||
if (icon.library === name) {
|
||||
if (icon.library === library.name) {
|
||||
icon.setIcon();
|
||||
}
|
||||
});
|
||||
@@ -61,15 +61,23 @@ export function registerIconLibrary(name: string, options: UnregisteredIconLibra
|
||||
* Convert the deep family → variant → icon name → markup cache that is more convenient to provide
|
||||
* to the flat URL → markup cache that icon libraries use internally
|
||||
**/
|
||||
function flattenIconLibraryCache(cache: IconLibraryFetched, resolver: IconLibraryResolver): IconLibraryCache<0> {
|
||||
return flatten(cache, {
|
||||
function addFetched(this: IconLibrary | UnregisteredIconLibrary, cache: IconLibraryFetched) {
|
||||
let { resolver, getKey } = this;
|
||||
|
||||
// Convert flat URL → markup cache to deep family → variant → icon name → markup cache
|
||||
let flatCache = flatten(cache, {
|
||||
getKey(path: string[]) {
|
||||
// name is always the last value no matter the depth
|
||||
let name = path.pop()!;
|
||||
let [family, variant] = path;
|
||||
return resolver(name, family, variant);
|
||||
let url = resolver(name, family, variant);
|
||||
let key = getKey?.(url) ?? url;
|
||||
return key;
|
||||
},
|
||||
}) as IconLibraryCache<0>;
|
||||
|
||||
this.fetched ??= {};
|
||||
Object.assign(this.fetched, flatCache);
|
||||
}
|
||||
|
||||
/** Removes an icon library from the registry. */
|
||||
|
||||
10
src/components/icon/types.d.ts
vendored
10
src/components/icon/types.d.ts
vendored
@@ -1,4 +1,5 @@
|
||||
export type IconLibraryResolver = (name: string, family: string, variant: string) => string;
|
||||
export type IconLibraryGetKey = (name: string) => string;
|
||||
export type IconLibraryMutator = (svg: SVGElement) => void;
|
||||
|
||||
// This is a utility for decrementing a number up to 3 by one
|
||||
@@ -20,8 +21,10 @@ export type IconLibraryCache<N extends number = 0> = Record<
|
||||
export type IconLibraryFetched = IconLibraryCache<0> | IconLibraryCache<1> | IconLibraryCache<2>;
|
||||
|
||||
export interface UnregisteredIconLibrary {
|
||||
name: string;
|
||||
resolver: IconLibraryResolver;
|
||||
mutator?: IconLibraryMutator;
|
||||
getKey?: IconLibraryGetKey;
|
||||
spriteSheet?: boolean;
|
||||
|
||||
// Max depth: family → variant → icon name → markup
|
||||
@@ -30,12 +33,7 @@ export interface UnregisteredIconLibrary {
|
||||
}
|
||||
|
||||
// Registered icon library
|
||||
export interface IconLibrary {
|
||||
name: string;
|
||||
resolver: IconLibraryResolver;
|
||||
mutator?: IconLibraryMutator;
|
||||
spriteSheet?: boolean;
|
||||
|
||||
export interface IconLibrary extends UnregisteredIconLibrary {
|
||||
// One level only: URL → markup
|
||||
fetched?: IconLibraryCache<0>;
|
||||
addFetched: (cache: IconLibraryFetched) => void;
|
||||
|
||||
@@ -95,10 +95,6 @@ function _deepEach(value: any, callback: EachCallback, options: EachOptions, pat
|
||||
}
|
||||
}
|
||||
|
||||
export type DeepEntriesOptions = {
|
||||
filter?: EachCallback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Like Object.entries, but for deeply nested objects.
|
||||
* For shallow objects the output is the same as Object.entries.
|
||||
@@ -109,17 +105,20 @@ export type DeepEntriesOptions = {
|
||||
* @returns Array of arrays. In each item, the last value is the value, and all values before that are the keys.
|
||||
* So for an object with N levels, the result will be an array of arrays with N+1 items each.
|
||||
*/
|
||||
export function deepEntries(obj: any, options: DeepEntriesOptions = {}): any[][] {
|
||||
let { filter } = options;
|
||||
export function deepEntries(obj: any, options?: EachOptions): any[][] {
|
||||
let entries: any[][] = [];
|
||||
deepEach(
|
||||
obj,
|
||||
(value, path) => {
|
||||
if (Array.isArray(value) || isPlainObject(value)) {
|
||||
// We only want leaf values
|
||||
return;
|
||||
}
|
||||
|
||||
deepEach(obj, (value, path, parent) => {
|
||||
let included = filter?.(value, path, parent) ?? true;
|
||||
|
||||
if (included) {
|
||||
entries.push([...path, value]);
|
||||
}
|
||||
});
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
return entries;
|
||||
}
|
||||
@@ -144,16 +143,18 @@ export function flatten<T = unknown>(
|
||||
return {} as Record<keyof any, T>;
|
||||
}
|
||||
|
||||
let entries = deepEntries(obj).map(pathAndValue => {
|
||||
if (pathAndValue.length < 2) {
|
||||
return null;
|
||||
}
|
||||
let entries = deepEntries(obj)
|
||||
.map(pathAndValue => {
|
||||
if (pathAndValue.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let value = pathAndValue.pop() as T;
|
||||
let path = pathAndValue as Property[];
|
||||
let key = getKey(path, value);
|
||||
return [key, value];
|
||||
}) as [string, T][];
|
||||
let value = pathAndValue.pop() as T;
|
||||
let path = pathAndValue as Property[];
|
||||
let key = getKey(path, value);
|
||||
return [key, value];
|
||||
})
|
||||
.filter(Boolean) as [string, T][];
|
||||
|
||||
return Object.fromEntries(entries.filter(Boolean)) as Record<keyof any, T>;
|
||||
return Object.fromEntries(entries) as Record<keyof any, T>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user