@charcoal-ui/icons 1.0.1-alpha.3 → 1.1.0-alpha.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.module.js","sources":["../src/loaders.ts","../src/ssr.ts","../src/PixivIcon.ts","../src/filenames.ts","../src/index.ts"],"sourcesContent":["const cache = new Map<string, string>()\n\ninterface SvgModule {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n __esModule: true\n default: string\n}\n\nexport class PixivIconLoadError extends Error {\n constructor(message?: string) {\n super(message)\n Object.setPrototypeOf(this, new.target)\n }\n}\n\nfunction createLoader(getPath: (attributeName: string) => Promise<string>) {\n return async function getSvgString(attributeName: string): Promise<string> {\n if (cache.has(attributeName)) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return cache.get(attributeName)!\n }\n\n const rawSvg = await getPath(attributeName)\n .then((path) => fetch(path))\n .then((response) => {\n if (!response.ok) {\n throw new PixivIconLoadError(\n `Failed to fetch <pixiv-icon name=\"${attributeName}\">`\n )\n }\n\n return response.text()\n })\n\n cache.set(attributeName, rawSvg)\n\n return rawSvg\n }\n}\n\nexport type Loader = ReturnType<typeof createLoader>\n\nconst loadFromRawUrl: Loader = createLoader((attributeName) =>\n Promise.resolve(attributeName)\n)\n\nconst loadFromFile: Loader = createLoader(async (attributeName) => {\n const [size, name] = attributeName.split('/')\n\n const { default: filename } = (await import(\n `../svg/${encodeURIComponent(size)}/${encodeURIComponent(name)}.svg`\n )) as SvgModule\n\n return filename\n})\n\nexport const loaders = { loadFromRawUrl, loadFromFile }\n","export const __SERVER__ = typeof window === 'undefined'\n\nconst CAN_USE_DOM = typeof HTMLElement !== 'undefined'\n\n/**\n * NOTICE: SSR 環境では `extends HTMLElement` できない\n *\n * @see https://github.com/vuejs/core/blob/9c304bfe7942a20264235865b4bb5f6e53fdee0d/packages/runtime-dom/src/apiCustomElement.ts#L143-L145\n */\nexport const BaseElement = (\n !__SERVER__ && CAN_USE_DOM ? HTMLElement : class {}\n) as typeof HTMLElement\n","import type React from 'react'\nimport warning from 'warning'\nimport { KnownIconFile } from './filenames'\nimport { loaders as defaultLoaders, Loader } from './loaders'\nimport { BaseElement, __SERVER__ } from './ssr'\nimport { sanitize } from 'dompurify'\n\nconst { loadFromFile, loadFromRawUrl } = defaultLoaders\n\nconst attributes = ['name', 'scale', 'unsafe-non-guideline-scale'] as const\n\nconst ROOT_MARGIN = 50\n\nexport interface KnownIconType extends Record<KnownIconFile, unknown> {}\n\nexport interface Props\n extends Omit<\n React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>,\n 'className' | 'css'\n > {\n name: keyof KnownIconType\n scale?: 1 | 2 | 3 | '1' | '2' | '3'\n 'unsafe-non-guideline-scale'?: number | string\n\n // CustomElements は className が使えない。class と書く必要がある\n // https://ja.reactjs.org/docs/web-components.html#using-web-components-in-react\n class?: string\n}\n\nconst loaders = new Map<string, Loader>()\n\ntype ExtendedIconFile = Exclude<keyof KnownIconType, KnownIconFile>\ntype Extended = [ExtendedIconFile] extends [never] // NOTE: ExtendedIconFileがneverならKnownIconTypeは拡張されていない\n ? false\n : true\n\nexport class PixivIcon extends BaseElement {\n static readonly tagName = 'pixiv-icon'\n\n static extend(\n map: Extended extends true\n ? Record<ExtendedIconFile, string>\n : Record<string, string>\n ) {\n warning(!__SERVER__, 'Using `PixivIcon.extend()` on server has no effect')\n if (__SERVER__) {\n return\n }\n\n Object.entries(map).forEach(([name, url]) => {\n if (!name.includes('/')) {\n throw new TypeError(\n `${name} is not a valid icon name. \"name\" must be named like [size]/[Name].`\n )\n }\n\n loaders.set(name, function customLoader() {\n return loadFromRawUrl(url)\n })\n })\n }\n\n static get observedAttributes() {\n return attributes\n }\n\n private svgContent?: string\n private observer?: IntersectionObserver\n\n get props() {\n const partial = Object.fromEntries(\n attributes.map((attribute) => [attribute, this.getAttribute(attribute)])\n ) as Record<typeof attributes[number], string | null>\n\n const name = partial.name\n\n if (name === null) {\n throw new TypeError('property \"name\" is required.')\n }\n\n if (!name.includes('/')) {\n throw new TypeError(\n `${name} is not a valid icon name. \"name\" must be named like [size]/[Name].`\n )\n }\n\n return {\n ...partial,\n name,\n }\n }\n\n get forceResizedSize() {\n if (this.props['unsafe-non-guideline-scale'] === null) {\n return null\n }\n\n const [size] = this.props.name.split('/')\n const scale = Number(this.props['unsafe-non-guideline-scale'])\n\n switch (size) {\n case 'Inline': {\n return 16 * scale\n }\n\n default: {\n return Number(size) * scale\n }\n }\n }\n\n get scaledSize() {\n const [size] = this.props.name.split('/')\n\n const scale = Number(this.props.scale ?? '1')\n\n switch (size) {\n case 'Inline': {\n switch (scale) {\n case 2: {\n return 32\n }\n\n default: {\n return 16\n }\n }\n }\n\n case '24': {\n return Number(size) * scale\n }\n\n default: {\n return Number(size)\n }\n }\n }\n\n constructor() {\n super()\n this.attachShadow({ mode: 'open' })\n }\n\n connectedCallback() {\n this.render()\n this.update()\n }\n\n disconnectedCallback() {\n this.observer?.disconnect()\n this.observer = undefined\n }\n\n attributeChangedCallback() {\n this.update()\n }\n\n render() {\n const size = this.forceResizedSize ?? this.scaledSize\n\n const style = sanitize(\n `<style>\n :host {\n display: inline-flex;\n --size: ${size}px;\n }\n\n svg {\n width: var(--size);\n height: var(--size);\n }\n</style>`,\n { ALLOWED_TAGS: ['style'], FORCE_BODY: true }\n )\n\n const svg = sanitize(\n this.svgContent !== undefined\n ? this.svgContent\n : `<svg viewBox=\"0 0 ${size} ${size}\"></svg>`,\n { USE_PROFILES: { svg: true, svgFilters: true } }\n )\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.shadowRoot!.innerHTML = style + svg\n }\n\n private update() {\n void this.waitUntilVisible().then(async () => {\n const { name } = this.props\n const loader = loaders.get(name) ?? loadFromFile\n\n this.svgContent = await loader(name)\n this.render()\n })\n }\n\n private waitUntilVisible() {\n return new Promise<void>((resolve) => {\n this.observer = new IntersectionObserver(\n ([first]) => {\n if (first.isIntersecting) {\n this.observer?.disconnect()\n this.observer = undefined\n resolve()\n }\n },\n { rootMargin: `${ROOT_MARGIN}px` }\n )\n\n this.observer.observe(this)\n })\n }\n}\n","/** This file is auto generated. DO NOT EDIT BY HAND. */\n\n/* eslint-disable */\n\n// prettier-ignore\nexport const KNOWN_ICON_FILES = [\"32/ShareIos\",\"32/Dot\",\"32/Message\",\"32/Gift\",\"32/Close\",\"32/Prev\",\"32/Next\",\"32/PullDown\",\"32/PullUp\",\"32/Expand\",\"32/Collapse\",\"32/Edit\",\"32/ShareAndroid\",\"32/BookmarkOff\",\"32/BookmarkOn\",\"32/NovelViewerSettings\",\"32/CommentOff\",\"32/CommentOn\",\"32/Home\",\"32/Index\",\"32/Serif\",\"32/Camera\",\"32/Delete\",\"32/Pan\",\"32/RollVertical\",\"32/RollHorizontal\",\"32/ZoomIn\",\"32/Upload\",\"32/Shopping\",\"32/User\",\"32/HorizontalWriting\",\"32/Notification\",\"32/SansSerif\",\"32/VerticalWriting\",\"32/NotificationOff\",\"24/Expand\",\"24/Collapse\",\"24/Close\",\"24/Pencil\",\"24/PencilDraw\",\"24/PencilText\",\"24/Reorder\",\"24/Add\",\"24/Subtract\",\"24/Star\",\"24/Check\",\"24/Send\",\"24/LockLock\",\"24/LockUnlock\",\"24/Up\",\"24/Down\",\"24/ReloadLoop\",\"24/Reload\",\"24/Options\",\"24/CommentOutline\",\"24/Trash\",\"24/TrashAlt\",\"24/ShareIos\",\"24/ViewGrid3Columns\",\"24/ViewGrid2Columns\",\"24/ViewList\",\"24/Upload\",\"24/UploadAlt\",\"24/DownloadAlt\",\"24/ArrowUp\",\"24/LikeOff\",\"24/LikeOn\",\"24/ArrowDown\",\"24/OptionsAlt\",\"24/PencilLive\",\"24/OpenInNew\",\"24/Settings\",\"24/Roll\",\"24/Calendar\",\"24/Save\",\"24/Logout\",\"24/CommentFill\",\"24/ShareAndroid\",\"16/More\",\"16/Back\",\"16/Search\",\"16/Add\",\"16/Check\",\"16/Menu\",\"16/Dot\",\"16/Book\",\"16/ArrowDown\",\"16/Speaker\",\"16/Remove\",\"16/BookmarkOn\",\"16/BookmarkOff\",\"16/Filter\",\"16/Error\",\"16/Info\",\"16/Pencil\",\"Inline/LikeOff\",\"Inline/View\",\"Inline/BookmarkOff\",\"Inline/BookmarkOn\",\"Inline/Breadcrumbs\",\"Inline/More\",\"Inline/ImageResponse\",\"Inline/Lock\",\"Inline/External\",\"Inline/Location\",\"Inline/View\",\"Inline/Like\",\"Inline/Smile\",\"Inline/SmileOn\",\"Inline/Check\",\"Inline/List\",\"Inline/ContextMenu\",\"Inline/Add\",\"Inline/Remove\",\"Inline/Images\",\"Inline/Nextworks\",\"Inline/Filter\",\"Inline/Users\",\"24/Next\",\"24/PullDown\",\"24/PullUp\",\"24/Prev\",\"24/Dot\",\"24/Notification\",\"24/NotificationOff\",\"24/Search\",\"24/Menu\",\"24/Index\",\"24/Services\",\"24/Error\",\"24/Home\",\"24/Warning\",\"24/Info\",\"24/QuestionOutline\",\"24/ChatBot\",\"24/ErrorOctagon\",\"24/Question\",\"24/Alart\",\"24/DotAlt\",\"24/ImageHidden\",\"24/AddRubi\",\"24/ImageReplace\",\"24/Text\",\"24/AddText\",\"24/FormatAlignRight\",\"24/FormatAlignLeft\",\"24/FormatAlignCenter\",\"24/AddImage\",\"24/ImgContain\",\"24/ImgCover\",\"24/BringForward\",\"24/BringBackward\",\"24/RotateRight\",\"24/Palette\",\"24/Duplicate\",\"24/FormatLineSpacing\",\"24/FormatLetterSpacing\",\"24/Rotate90DegreesCc\",\"24/Rotate90DegreesC\",\"24/FormatFontSize\",\"24/FormatFontFamily\",\"24/FormatColorFill\",\"24/FormatColorFillNoColor\",\"24/ChromaticAberration\",\"24/Move1\",\"24/Play\",\"24/Pause\",\"24/Microphone\",\"24/PauseAlt\",\"24/CameraVideo\",\"24/Camera\",\"24/Fov\",\"24/Sun\",\"24/Shutter\",\"24/Contrast\",\"24/FrameEffect\",\"24/Hue\",\"24/Saturation\",\"24/Glow\",\"24/Binet\",\"24/Overlay\",\"24/Flare\",\"24/Dust\",\"24/Temperature\",\"24/Filter\",\"24/Usagi\",\"24/Set\",\"24/Gift\",\"24/MobilePhone\",\"24/Users\",\"24/AddPeople\",\"24/Person\",\"24/Emoji\",\"24/Shopping\",\"24/UsagiAlt\",\"24/Fashion\",\"24/FaceEdit\",\"24/DeviceRotation\",\"24/Ar\",\"24/FrameSize\",\"24/Body\",\"24/BodyEdit\",\"24/HairEdit\",\"24/ItemRemove\",\"24/AddModel\",\"24/ChangeCharactor\",\"24/Pose\",\"24/Show\",\"24/ShowOutline\",\"24/Hide\",\"24/Idea\",\"24/Book\",\"24/Message\",\"24/Images\",\"24/Image\",\"24/Video\",\"24/Hashtag\",\"24/ImageAlt\",\"24/Discovery\",\"24/Description\",\"24/File\",\"24/Archive\",\"24/Invoice\",\"24/Announcement\",\"24/Link\",\"24/Manga\",\"24/Novels\",\"24/Contest\",\"24/LatestWorks\",\"24/Ranking\",\"24/Feed\",\"24/Groups\",\"24/Events\",\"24/Projects\",\"24/Codes\",\"24/Invalid\",\"32/LikeOff\",\"32/LikeOn\",\"32/LikeOnPrivate\",\"24/ImageHidden\",\"Inline/Folder\",\"Inline/Pencil\",\"Inline/Comment\"] as const;\n\nexport type KnownIconFile = typeof KNOWN_ICON_FILES[number]\n\n/* eslint-enable */\n","import { PixivIcon, Props } from './PixivIcon'\nimport { __SERVER__ } from './ssr'\nexport { PixivIcon, type KnownIconType, type Props } from './PixivIcon'\nexport { KNOWN_ICON_FILES } from './filenames'\nexport { PixivIconLoadError } from './loaders'\n\ndeclare global {\n interface Window {\n PixivIcon: typeof PixivIcon\n }\n\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'pixiv-icon': Props\n }\n }\n}\n\nif (!__SERVER__) {\n // TODO: HMR対応\n if (!window.customElements.get(PixivIcon.tagName)) {\n window.PixivIcon = PixivIcon\n window.customElements.define(PixivIcon.tagName, PixivIcon)\n }\n}\n"],"names":["cache","Map","PixivIconLoadError","message","_this","Object","setPrototypeOf","Error","createLoader","getPath","attributeName","has","get","then","path","fetch","response","ok","text","rawSvg","set","loaders","loadFromRawUrl","Promise","resolve","loadFromFile","split","name","import","encodeURIComponent","__SERVER__","window","CAN_USE_DOM","HTMLElement","BaseElement","defaultLoaders","attributes","PixivIcon","svgContent","observer","attachShadow","mode","extend","map","warning","entries","forEach","url","includes","TypeError","connectedCallback","this","render","update","disconnectedCallback","disconnect","undefined","attributeChangedCallback","size","forceResizedSize","scaledSize","style","sanitize","ALLOWED_TAGS","FORCE_BODY","svg","USE_PROFILES","svgFilters","shadowRoot","innerHTML","waitUntilVisible","_this2","props","loader","_this3","IntersectionObserver","isIntersecting","rootMargin","ROOT_MARGIN","observe","partial","fromEntries","attribute","_this4","getAttribute","scale","Number","tagName","KNOWN_ICON_FILES","customElements","define"],"mappings":"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"}
1
+ {"version":3,"file":"index.module.js","sources":["../src/BaseLoader.ts","../src/loaders.ts","../src/ssr.ts","../src/PixivIcon.ts","../src/filenames.ts","../src/index.ts"],"sourcesContent":["export class PixivIconLoadError extends Error {\n constructor(message?: string) {\n super(message)\n Object.setPrototypeOf(this, new.target)\n }\n}\n\n/**\n * SVG アイコンを取得するクラス\n *\n * 一度リクエストされたアイコンは(リクエスト中のも含め)何度もリクエストしないようになっている\n */\nexport class BaseLoader {\n private _name: string\n private _getSource: () => Promise<string>\n\n private _promise: Promise<string> | undefined = undefined\n private _resultSvg: string | undefined = undefined\n\n constructor(name: string, getSource: () => Promise<string>) {\n this._name = name\n this._getSource = getSource\n }\n\n isLoading() {\n return this._promise !== undefined\n }\n\n async fetch() {\n if (this._resultSvg !== undefined) {\n return this._resultSvg\n }\n\n if (this._promise) {\n return this._promise\n }\n\n this._promise = this._getSource()\n .then((src) => fetch(src))\n .then((response) => {\n if (!response.ok) {\n throw new PixivIconLoadError(\n `Failed to fetch <pixiv-icon name=\"${this._name}\">`\n )\n }\n\n return response.text()\n })\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n\nexport interface Loadable {\n fetch(): Promise<string>\n isLoading(): boolean\n}\n","import { Loadable, BaseLoader } from './BaseLoader'\n\n/**\n * オブジェクトプール。Loader のインスタンスは作り次第ここに入れる\n *\n * 同じアイコンへの複数回のリクエストが起きないためには、Loader がこの中でユニークでないと行けない\n */\nconst pool = new Map<string, Loadable>()\n\n/**\n * アイコンを特定の URL から取得する Loader\n */\nexport class UrlLoader implements Loadable {\n private fetcher: BaseLoader\n\n static find(name: string) {\n return pool.get(name)\n }\n\n static register(name: string, url: string) {\n const loader = new UrlLoader(name, url)\n\n pool.set(name, loader)\n }\n\n private constructor(name: string, url: string) {\n this.fetcher = new BaseLoader(name, () => Promise.resolve(url))\n }\n\n fetch(): Promise<string> {\n return this.fetcher.fetch()\n }\n\n isLoading() {\n return this.fetcher.isLoading()\n }\n}\n\n/**\n * アイコン名から import すべきファイル名( このパッケージ内にある )を解決してくる Loader\n */\nexport class FileLoader implements Loadable {\n private fetcher: BaseLoader\n\n static findOrRegister(name: string) {\n const registeredLoader = pool.get(name)\n if (registeredLoader) {\n return registeredLoader\n }\n\n const newLoader = new FileLoader(name)\n pool.set(name, newLoader)\n\n return newLoader\n }\n\n private constructor(name: string) {\n this.fetcher = new BaseLoader(name, () => getSourceFromName(name))\n }\n\n fetch(): Promise<string> {\n return this.fetcher.fetch()\n }\n\n isLoading() {\n return this.fetcher.isLoading()\n }\n}\n\ninterface SvgModule {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n __esModule: true\n default: string\n}\n\nasync function getSourceFromName(fullName: string) {\n const [size, name] = fullName.split('/')\n\n const { default: filename } = (await import(\n `../svg/${encodeURIComponent(size)}/${encodeURIComponent(name)}.svg`\n )) as SvgModule\n\n return filename\n}\n","export const __SERVER__ = typeof window === 'undefined'\n\nconst CAN_USE_DOM = typeof HTMLElement !== 'undefined'\n\n// Workaround: `extends HTMLELement` の形式でないとbabelのトランスパイルがおかしくなる\nif (__SERVER__ || !CAN_USE_DOM) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any\n globalThis.HTMLElement = class {} as any\n}\n","import type React from 'react'\nimport warning from 'warning'\nimport { KnownIconFile } from './filenames'\nimport { FileLoader, UrlLoader } from './loaders'\nimport { __SERVER__ } from './ssr'\nimport { sanitize } from 'dompurify'\n\nconst attributes = ['name', 'scale', 'unsafe-non-guideline-scale'] as const\n\nconst ROOT_MARGIN = 50\n\nexport interface KnownIconType extends Record<KnownIconFile, unknown> {}\n\nexport interface Props\n extends Omit<\n React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>,\n 'className' | 'css'\n > {\n name: keyof KnownIconType\n scale?: 1 | 2 | 3 | '1' | '2' | '3'\n 'unsafe-non-guideline-scale'?: number | string\n\n // CustomElements は className が使えない。class と書く必要がある\n // https://ja.reactjs.org/docs/web-components.html#using-web-components-in-react\n class?: string\n}\n\ntype ExtendedIconFile = Exclude<keyof KnownIconType, KnownIconFile>\ntype Extended = [ExtendedIconFile] extends [never] // NOTE: ExtendedIconFileがneverならKnownIconTypeは拡張されていない\n ? false\n : true\n\nexport class PixivIcon extends HTMLElement {\n static readonly tagName = 'pixiv-icon'\n\n static extend(\n map: Extended extends true\n ? Record<ExtendedIconFile, string>\n : Record<string, string>\n ) {\n warning(!__SERVER__, 'Using `PixivIcon.extend()` on server has no effect')\n if (__SERVER__) {\n return\n }\n\n Object.entries(map).forEach(([name, url]) => {\n if (!name.includes('/')) {\n throw new TypeError(\n `${name} is not a valid icon name. \"name\" must be named like [size]/[Name].`\n )\n }\n\n UrlLoader.register(name, url)\n })\n }\n\n static get observedAttributes() {\n return attributes\n }\n\n private svgContent?: string\n private observer?: IntersectionObserver\n private isVisible = false\n\n get props() {\n const partial = Object.fromEntries(\n attributes.map((attribute) => [attribute, this.getAttribute(attribute)])\n ) as Record<typeof attributes[number], string | null>\n\n const name = partial.name\n\n if (name === null) {\n throw new TypeError('property \"name\" is required.')\n }\n\n if (!name.includes('/')) {\n throw new TypeError(\n `${name} is not a valid icon name. \"name\" must be named like [size]/[Name].`\n )\n }\n\n return {\n ...partial,\n name,\n }\n }\n\n get forceResizedSize() {\n if (this.props['unsafe-non-guideline-scale'] === null) {\n return null\n }\n\n const [size] = this.props.name.split('/')\n const scale = Number(this.props['unsafe-non-guideline-scale'])\n\n switch (size) {\n case 'Inline': {\n return 16 * scale\n }\n\n default: {\n return Number(size) * scale\n }\n }\n }\n\n get scaledSize() {\n const [size] = this.props.name.split('/')\n\n const scale = Number(this.props.scale ?? '1')\n\n switch (size) {\n case 'Inline': {\n switch (scale) {\n case 2: {\n return 32\n }\n\n default: {\n return 16\n }\n }\n }\n\n case '24': {\n return Number(size) * scale\n }\n\n default: {\n return Number(size)\n }\n }\n }\n\n constructor() {\n super()\n this.attachShadow({ mode: 'open' })\n }\n\n async connectedCallback() {\n this.render()\n await this.waitUntilVisible()\n this.isVisible = true\n await this.loadSvg(this.props.name)\n }\n\n disconnectedCallback() {\n this.observer?.disconnect()\n this.observer = undefined\n this.isVisible = false\n }\n\n attributeChangedCallback(\n attr: string,\n _oldValue: string | null,\n newValue: string\n ) {\n // 非表示の場合はロードしない\n if (!this.isVisible) {\n return\n }\n\n // name が変更された場合必ず再読み込みを試みる\n if (attr === 'name') {\n void this.loadSvg(newValue)\n return\n }\n\n // SVG が読み込み済み && scale などの変更だけならそこだけ反映すればいい\n if (this.svgContent !== undefined) {\n this.render()\n return\n }\n\n // まだ SVG が読み込めてないなら load\n void this.loadSvg(this.props.name)\n }\n\n render() {\n const size = this.forceResizedSize ?? this.scaledSize\n\n const style = sanitize(\n `<style>\n :host {\n display: inline-flex;\n --size: ${size}px;\n }\n\n svg {\n width: var(--size);\n height: var(--size);\n }\n</style>`,\n { ALLOWED_TAGS: ['style'], FORCE_BODY: true }\n )\n\n const svg = sanitize(\n this.svgContent !== undefined\n ? this.svgContent\n : `<svg viewBox=\"0 0 ${size} ${size}\"></svg>`,\n { USE_PROFILES: { svg: true, svgFilters: true } }\n )\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.shadowRoot!.innerHTML = style + svg\n }\n\n private async loadSvg(name: string) {\n const loader = UrlLoader.find(name) ?? FileLoader.findOrRegister(name)\n\n this.svgContent = await loader.fetch()\n this.render()\n }\n\n private waitUntilVisible() {\n return new Promise<void>((resolve) => {\n this.observer = new IntersectionObserver(\n ([first]) => {\n if (first.isIntersecting) {\n this.observer?.disconnect()\n this.observer = undefined\n resolve()\n }\n },\n { rootMargin: `${ROOT_MARGIN}px` }\n )\n\n this.observer.observe(this)\n })\n }\n}\n","/** This file is auto generated. DO NOT EDIT BY HAND. */\n\nexport const KNOWN_ICON_FILES = [\"32/ShareIos\",\"32/Dot\",\"32/Message\",\"32/Gift\",\"32/Close\",\"32/Prev\",\"32/Next\",\"32/PullDown\",\"32/PullUp\",\"32/Expand\",\"32/Collapse\",\"32/Edit\",\"32/ShareAndroid\",\"32/BookmarkOff\",\"32/BookmarkOn\",\"32/NovelViewerSettings\",\"32/CommentOff\",\"32/CommentOn\",\"32/Home\",\"32/Index\",\"32/Serif\",\"32/Camera\",\"32/Delete\",\"32/Pan\",\"32/RollVertical\",\"32/RollHorizontal\",\"32/ZoomIn\",\"32/Upload\",\"32/Shopping\",\"32/User\",\"32/HorizontalWriting\",\"32/Notification\",\"32/SansSerif\",\"32/VerticalWriting\",\"32/NotificationOff\",\"24/Expand\",\"24/Collapse\",\"24/Close\",\"24/Pencil\",\"24/PencilDraw\",\"24/PencilText\",\"24/Reorder\",\"24/Add\",\"24/Subtract\",\"24/Star\",\"24/Check\",\"24/Send\",\"24/LockLock\",\"24/LockUnlock\",\"24/Up\",\"24/Down\",\"24/ReloadLoop\",\"24/Reload\",\"24/Options\",\"24/CommentOutline\",\"24/Trash\",\"24/TrashAlt\",\"24/ShareIos\",\"24/ViewGrid3Columns\",\"24/ViewGrid2Columns\",\"24/ViewList\",\"24/Upload\",\"24/UploadAlt\",\"24/DownloadAlt\",\"24/ArrowUp\",\"24/LikeOff\",\"24/LikeOn\",\"24/ArrowDown\",\"24/OptionsAlt\",\"24/PencilLive\",\"24/OpenInNew\",\"24/Settings\",\"24/Roll\",\"24/Calendar\",\"24/Save\",\"24/Logout\",\"24/CommentFill\",\"24/ShareAndroid\",\"16/More\",\"16/Back\",\"16/Search\",\"16/Add\",\"16/Check\",\"16/Menu\",\"16/Dot\",\"16/Book\",\"16/ArrowDown\",\"16/Speaker\",\"16/Remove\",\"16/BookmarkOn\",\"16/BookmarkOff\",\"16/Filter\",\"16/Error\",\"16/Info\",\"16/Pencil\",\"Inline/LikeOff\",\"Inline/View\",\"Inline/BookmarkOff\",\"Inline/BookmarkOn\",\"Inline/Breadcrumbs\",\"Inline/More\",\"Inline/ImageResponse\",\"Inline/Lock\",\"Inline/External\",\"Inline/Location\",\"Inline/View\",\"Inline/Like\",\"Inline/Smile\",\"Inline/SmileOn\",\"Inline/Check\",\"Inline/List\",\"Inline/ContextMenu\",\"Inline/Add\",\"Inline/Remove\",\"Inline/Images\",\"Inline/Nextworks\",\"Inline/Filter\",\"Inline/Users\",\"24/Next\",\"24/PullDown\",\"24/PullUp\",\"24/Prev\",\"24/Dot\",\"24/Notification\",\"24/NotificationOff\",\"24/Search\",\"24/Menu\",\"24/Index\",\"24/Services\",\"24/Error\",\"24/Home\",\"24/Warning\",\"24/Info\",\"24/QuestionOutline\",\"24/ChatBot\",\"24/ErrorOctagon\",\"24/Question\",\"24/Alart\",\"24/DotAlt\",\"24/ImageHidden\",\"24/AddRubi\",\"24/ImageReplace\",\"24/Text\",\"24/AddText\",\"24/FormatAlignRight\",\"24/FormatAlignLeft\",\"24/FormatAlignCenter\",\"24/AddImage\",\"24/ImgContain\",\"24/ImgCover\",\"24/BringForward\",\"24/BringBackward\",\"24/RotateRight\",\"24/Palette\",\"24/Duplicate\",\"24/FormatLineSpacing\",\"24/FormatLetterSpacing\",\"24/Rotate90DegreesCc\",\"24/Rotate90DegreesC\",\"24/FormatFontSize\",\"24/FormatFontFamily\",\"24/FormatColorFill\",\"24/FormatColorFillNoColor\",\"24/ChromaticAberration\",\"24/Move1\",\"24/Play\",\"24/Pause\",\"24/Microphone\",\"24/PauseAlt\",\"24/CameraVideo\",\"24/Camera\",\"24/Fov\",\"24/Sun\",\"24/Shutter\",\"24/Contrast\",\"24/FrameEffect\",\"24/Hue\",\"24/Saturation\",\"24/Glow\",\"24/Binet\",\"24/Overlay\",\"24/Flare\",\"24/Dust\",\"24/Temperature\",\"24/Filter\",\"24/Usagi\",\"24/Set\",\"24/Gift\",\"24/MobilePhone\",\"24/Users\",\"24/AddPeople\",\"24/Person\",\"24/Emoji\",\"24/Shopping\",\"24/UsagiAlt\",\"24/Fashion\",\"24/FaceEdit\",\"24/DeviceRotation\",\"24/Ar\",\"24/FrameSize\",\"24/Body\",\"24/BodyEdit\",\"24/HairEdit\",\"24/ItemRemove\",\"24/AddModel\",\"24/ChangeCharactor\",\"24/Pose\",\"24/Show\",\"24/ShowOutline\",\"24/Hide\",\"24/Idea\",\"24/Book\",\"24/Message\",\"24/Images\",\"24/Image\",\"24/Video\",\"24/Hashtag\",\"24/ImageAlt\",\"24/Discovery\",\"24/Description\",\"24/File\",\"24/Archive\",\"24/Invoice\",\"24/Announcement\",\"24/Link\",\"24/Manga\",\"24/Novels\",\"24/Contest\",\"24/LatestWorks\",\"24/Ranking\",\"24/Feed\",\"24/Groups\",\"24/Events\",\"24/Projects\",\"24/Codes\",\"24/Invalid\",\"32/LikeOff\",\"32/LikeOn\",\"32/LikeOnPrivate\",\"24/ImageHidden\",\"Inline/Folder\",\"Inline/Pencil\",\"Inline/Comment\",\"16/View\",\"16/ImageResponse\",\"16/Like\",\"16/Smile\",\"16/Comment\",\"16/Ranking\",\"16/Artwork\",\"24/NoImage\"] as const;\n\nexport type KnownIconFile = typeof KNOWN_ICON_FILES[number];\n","import { PixivIcon, Props } from './PixivIcon'\nimport { __SERVER__ } from './ssr'\nexport { PixivIcon, type KnownIconType, type Props } from './PixivIcon'\nexport { KNOWN_ICON_FILES } from './filenames'\nexport { PixivIconLoadError } from './BaseLoader'\n\ndeclare global {\n interface Window {\n PixivIcon: typeof PixivIcon\n }\n\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'pixiv-icon': Props\n }\n }\n}\n\nif (!__SERVER__) {\n // TODO: HMR対応\n if (!window.customElements.get(PixivIcon.tagName)) {\n window.PixivIcon = PixivIcon\n window.customElements.define(PixivIcon.tagName, PixivIcon)\n }\n}\n"],"names":["PixivIconLoadError","message","_this","Object","setPrototypeOf","Error","BaseLoader","name","getSource","_name","_getSource","_promise","undefined","_resultSvg","this","isLoading","fetch","_this3","then","src","response","ok","text","svg","pool","Map","UrlLoader","url","fetcher","Promise","resolve","find","get","register","loader","set","FileLoader","fullName","split","import","encodeURIComponent","getSourceFromName","findOrRegister","registeredLoader","newLoader","__SERVER__","window","CAN_USE_DOM","HTMLElement","globalThis","attributes","PixivIcon","svgContent","observer","isVisible","attachShadow","mode","extend","map","warning","entries","forEach","includes","TypeError","connectedCallback","render","waitUntilVisible","loadSvg","props","disconnectedCallback","disconnect","attributeChangedCallback","attr","_oldValue","newValue","size","forceResizedSize","scaledSize","style","sanitize","ALLOWED_TAGS","FORCE_BODY","USE_PROFILES","svgFilters","shadowRoot","innerHTML","_this5","_this6","IntersectionObserver","isIntersecting","rootMargin","ROOT_MARGIN","observe","partial","fromEntries","attribute","_this7","getAttribute","scale","Number","tagName","KNOWN_ICON_FILES","customElements","define"],"mappings":"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"}
package/dist/loaders.d.ts CHANGED
@@ -1,11 +1,23 @@
1
- export declare class PixivIconLoadError extends Error {
2
- constructor(message?: string);
1
+ import { Loadable } from './BaseLoader';
2
+ /**
3
+ * アイコンを特定の URL から取得する Loader
4
+ */
5
+ export declare class UrlLoader implements Loadable {
6
+ private fetcher;
7
+ static find(name: string): Loadable | undefined;
8
+ static register(name: string, url: string): void;
9
+ private constructor();
10
+ fetch(): Promise<string>;
11
+ isLoading(): boolean;
12
+ }
13
+ /**
14
+ * アイコン名から import すべきファイル名( このパッケージ内にある )を解決してくる Loader
15
+ */
16
+ export declare class FileLoader implements Loadable {
17
+ private fetcher;
18
+ static findOrRegister(name: string): Loadable;
19
+ private constructor();
20
+ fetch(): Promise<string>;
21
+ isLoading(): boolean;
3
22
  }
4
- declare function createLoader(getPath: (attributeName: string) => Promise<string>): (attributeName: string) => Promise<string>;
5
- export declare type Loader = ReturnType<typeof createLoader>;
6
- export declare const loaders: {
7
- loadFromRawUrl: (attributeName: string) => Promise<string>;
8
- loadFromFile: (attributeName: string) => Promise<string>;
9
- };
10
- export {};
11
23
  //# sourceMappingURL=loaders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loaders.d.ts","sourceRoot":"","sources":["../src/loaders.ts"],"names":[],"mappings":"AAQA,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,iBAAS,YAAY,CAAC,OAAO,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,mBACrB,MAAM,KAAG,QAAQ,MAAM,CAAC,CAsB3E;AAED,oBAAY,MAAM,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAA;AAgBpD,eAAO,MAAM,OAAO;oCAxCgC,MAAM,KAAG,QAAQ,MAAM,CAAC;kCAAxB,MAAM,KAAG,QAAQ,MAAM,CAAC;CAwCrB,CAAA"}
1
+ {"version":3,"file":"loaders.d.ts","sourceRoot":"","sources":["../src/loaders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAc,MAAM,cAAc,CAAA;AASnD;;GAEG;AACH,qBAAa,SAAU,YAAW,QAAQ;IACxC,OAAO,CAAC,OAAO,CAAY;IAE3B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM;IAIxB,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAMzC,OAAO;IAIP,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,SAAS;CAGV;AAED;;GAEG;AACH,qBAAa,UAAW,YAAW,QAAQ;IACzC,OAAO,CAAC,OAAO,CAAY;IAE3B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM;IAYlC,OAAO;IAIP,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,SAAS;CAGV"}
package/dist/ssr.d.ts CHANGED
@@ -1,11 +1,2 @@
1
1
  export declare const __SERVER__: boolean;
2
- /**
3
- * NOTICE: SSR 環境では `extends HTMLElement` できない
4
- *
5
- * @see https://github.com/vuejs/core/blob/9c304bfe7942a20264235865b4bb5f6e53fdee0d/packages/runtime-dom/src/apiCustomElement.ts#L143-L145
6
- */
7
- export declare const BaseElement: {
8
- new (): HTMLElement;
9
- prototype: HTMLElement;
10
- };
11
2
  //# sourceMappingURL=ssr.d.ts.map
package/dist/ssr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,SAAgC,CAAA;AAIvD;;;;GAIG;AACH,eAAO,MAAM,WAAW;;;CAED,CAAA"}
1
+ {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,SAAgC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charcoal-ui/icons",
3
- "version": "1.0.1-alpha.3",
3
+ "version": "1.1.0-alpha.2",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "source": "./src/index.ts",
@@ -45,5 +45,5 @@
45
45
  "url": "https://github.com/pixiv/charcoal.git",
46
46
  "directory": "packages/icons"
47
47
  },
48
- "gitHead": "0ee189a14a669c9e9f8eb6e08ee517054c6c3691"
48
+ "gitHead": "c61b0bdd25c3a26b9f27a6d33bc7a7219696ac77"
49
49
  }
@@ -0,0 +1,64 @@
1
+ export class PixivIconLoadError extends Error {
2
+ constructor(message?: string) {
3
+ super(message)
4
+ Object.setPrototypeOf(this, new.target)
5
+ }
6
+ }
7
+
8
+ /**
9
+ * SVG アイコンを取得するクラス
10
+ *
11
+ * 一度リクエストされたアイコンは(リクエスト中のも含め)何度もリクエストしないようになっている
12
+ */
13
+ export class BaseLoader {
14
+ private _name: string
15
+ private _getSource: () => Promise<string>
16
+
17
+ private _promise: Promise<string> | undefined = undefined
18
+ private _resultSvg: string | undefined = undefined
19
+
20
+ constructor(name: string, getSource: () => Promise<string>) {
21
+ this._name = name
22
+ this._getSource = getSource
23
+ }
24
+
25
+ isLoading() {
26
+ return this._promise !== undefined
27
+ }
28
+
29
+ async fetch() {
30
+ if (this._resultSvg !== undefined) {
31
+ return this._resultSvg
32
+ }
33
+
34
+ if (this._promise) {
35
+ return this._promise
36
+ }
37
+
38
+ this._promise = this._getSource()
39
+ .then((src) => fetch(src))
40
+ .then((response) => {
41
+ if (!response.ok) {
42
+ throw new PixivIconLoadError(
43
+ `Failed to fetch <pixiv-icon name="${this._name}">`
44
+ )
45
+ }
46
+
47
+ return response.text()
48
+ })
49
+ .then((svg) => {
50
+ this._resultSvg = svg
51
+ return this._resultSvg
52
+ })
53
+ .finally(() => {
54
+ this._promise = undefined
55
+ })
56
+
57
+ return this._promise
58
+ }
59
+ }
60
+
61
+ export interface Loadable {
62
+ fetch(): Promise<string>
63
+ isLoading(): boolean
64
+ }
package/src/PixivIcon.ts CHANGED
@@ -1,12 +1,10 @@
1
1
  import type React from 'react'
2
2
  import warning from 'warning'
3
3
  import { KnownIconFile } from './filenames'
4
- import { loaders as defaultLoaders, Loader } from './loaders'
5
- import { BaseElement, __SERVER__ } from './ssr'
4
+ import { FileLoader, UrlLoader } from './loaders'
5
+ import { __SERVER__ } from './ssr'
6
6
  import { sanitize } from 'dompurify'
7
7
 
8
- const { loadFromFile, loadFromRawUrl } = defaultLoaders
9
-
10
8
  const attributes = ['name', 'scale', 'unsafe-non-guideline-scale'] as const
11
9
 
12
10
  const ROOT_MARGIN = 50
@@ -27,14 +25,12 @@ export interface Props
27
25
  class?: string
28
26
  }
29
27
 
30
- const loaders = new Map<string, Loader>()
31
-
32
28
  type ExtendedIconFile = Exclude<keyof KnownIconType, KnownIconFile>
33
29
  type Extended = [ExtendedIconFile] extends [never] // NOTE: ExtendedIconFileがneverならKnownIconTypeは拡張されていない
34
30
  ? false
35
31
  : true
36
32
 
37
- export class PixivIcon extends BaseElement {
33
+ export class PixivIcon extends HTMLElement {
38
34
  static readonly tagName = 'pixiv-icon'
39
35
 
40
36
  static extend(
@@ -54,9 +50,7 @@ export class PixivIcon extends BaseElement {
54
50
  )
55
51
  }
56
52
 
57
- loaders.set(name, function customLoader() {
58
- return loadFromRawUrl(url)
59
- })
53
+ UrlLoader.register(name, url)
60
54
  })
61
55
  }
62
56
 
@@ -66,6 +60,7 @@ export class PixivIcon extends BaseElement {
66
60
 
67
61
  private svgContent?: string
68
62
  private observer?: IntersectionObserver
63
+ private isVisible = false
69
64
 
70
65
  get props() {
71
66
  const partial = Object.fromEntries(
@@ -142,18 +137,43 @@ export class PixivIcon extends BaseElement {
142
137
  this.attachShadow({ mode: 'open' })
143
138
  }
144
139
 
145
- connectedCallback() {
140
+ async connectedCallback() {
146
141
  this.render()
147
- this.update()
142
+ await this.waitUntilVisible()
143
+ this.isVisible = true
144
+ await this.loadSvg(this.props.name)
148
145
  }
149
146
 
150
147
  disconnectedCallback() {
151
148
  this.observer?.disconnect()
152
149
  this.observer = undefined
150
+ this.isVisible = false
153
151
  }
154
152
 
155
- attributeChangedCallback() {
156
- this.update()
153
+ attributeChangedCallback(
154
+ attr: string,
155
+ _oldValue: string | null,
156
+ newValue: string
157
+ ) {
158
+ // 非表示の場合はロードしない
159
+ if (!this.isVisible) {
160
+ return
161
+ }
162
+
163
+ // name が変更された場合必ず再読み込みを試みる
164
+ if (attr === 'name') {
165
+ void this.loadSvg(newValue)
166
+ return
167
+ }
168
+
169
+ // SVG が読み込み済み && scale などの変更だけならそこだけ反映すればいい
170
+ if (this.svgContent !== undefined) {
171
+ this.render()
172
+ return
173
+ }
174
+
175
+ // まだ SVG が読み込めてないなら load
176
+ void this.loadSvg(this.props.name)
157
177
  }
158
178
 
159
179
  render() {
@@ -185,14 +205,11 @@ export class PixivIcon extends BaseElement {
185
205
  this.shadowRoot!.innerHTML = style + svg
186
206
  }
187
207
 
188
- private update() {
189
- void this.waitUntilVisible().then(async () => {
190
- const { name } = this.props
191
- const loader = loaders.get(name) ?? loadFromFile
208
+ private async loadSvg(name: string) {
209
+ const loader = UrlLoader.find(name) ?? FileLoader.findOrRegister(name)
192
210
 
193
- this.svgContent = await loader(name)
194
- this.render()
195
- })
211
+ this.svgContent = await loader.fetch()
212
+ this.render()
196
213
  }
197
214
 
198
215
  private waitUntilVisible() {
package/src/filenames.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  /** This file is auto generated. DO NOT EDIT BY HAND. */
2
2
 
3
- /* eslint-disable */
3
+ export const KNOWN_ICON_FILES = ["32/ShareIos","32/Dot","32/Message","32/Gift","32/Close","32/Prev","32/Next","32/PullDown","32/PullUp","32/Expand","32/Collapse","32/Edit","32/ShareAndroid","32/BookmarkOff","32/BookmarkOn","32/NovelViewerSettings","32/CommentOff","32/CommentOn","32/Home","32/Index","32/Serif","32/Camera","32/Delete","32/Pan","32/RollVertical","32/RollHorizontal","32/ZoomIn","32/Upload","32/Shopping","32/User","32/HorizontalWriting","32/Notification","32/SansSerif","32/VerticalWriting","32/NotificationOff","24/Expand","24/Collapse","24/Close","24/Pencil","24/PencilDraw","24/PencilText","24/Reorder","24/Add","24/Subtract","24/Star","24/Check","24/Send","24/LockLock","24/LockUnlock","24/Up","24/Down","24/ReloadLoop","24/Reload","24/Options","24/CommentOutline","24/Trash","24/TrashAlt","24/ShareIos","24/ViewGrid3Columns","24/ViewGrid2Columns","24/ViewList","24/Upload","24/UploadAlt","24/DownloadAlt","24/ArrowUp","24/LikeOff","24/LikeOn","24/ArrowDown","24/OptionsAlt","24/PencilLive","24/OpenInNew","24/Settings","24/Roll","24/Calendar","24/Save","24/Logout","24/CommentFill","24/ShareAndroid","16/More","16/Back","16/Search","16/Add","16/Check","16/Menu","16/Dot","16/Book","16/ArrowDown","16/Speaker","16/Remove","16/BookmarkOn","16/BookmarkOff","16/Filter","16/Error","16/Info","16/Pencil","Inline/LikeOff","Inline/View","Inline/BookmarkOff","Inline/BookmarkOn","Inline/Breadcrumbs","Inline/More","Inline/ImageResponse","Inline/Lock","Inline/External","Inline/Location","Inline/View","Inline/Like","Inline/Smile","Inline/SmileOn","Inline/Check","Inline/List","Inline/ContextMenu","Inline/Add","Inline/Remove","Inline/Images","Inline/Nextworks","Inline/Filter","Inline/Users","24/Next","24/PullDown","24/PullUp","24/Prev","24/Dot","24/Notification","24/NotificationOff","24/Search","24/Menu","24/Index","24/Services","24/Error","24/Home","24/Warning","24/Info","24/QuestionOutline","24/ChatBot","24/ErrorOctagon","24/Question","24/Alart","24/DotAlt","24/ImageHidden","24/AddRubi","24/ImageReplace","24/Text","24/AddText","24/FormatAlignRight","24/FormatAlignLeft","24/FormatAlignCenter","24/AddImage","24/ImgContain","24/ImgCover","24/BringForward","24/BringBackward","24/RotateRight","24/Palette","24/Duplicate","24/FormatLineSpacing","24/FormatLetterSpacing","24/Rotate90DegreesCc","24/Rotate90DegreesC","24/FormatFontSize","24/FormatFontFamily","24/FormatColorFill","24/FormatColorFillNoColor","24/ChromaticAberration","24/Move1","24/Play","24/Pause","24/Microphone","24/PauseAlt","24/CameraVideo","24/Camera","24/Fov","24/Sun","24/Shutter","24/Contrast","24/FrameEffect","24/Hue","24/Saturation","24/Glow","24/Binet","24/Overlay","24/Flare","24/Dust","24/Temperature","24/Filter","24/Usagi","24/Set","24/Gift","24/MobilePhone","24/Users","24/AddPeople","24/Person","24/Emoji","24/Shopping","24/UsagiAlt","24/Fashion","24/FaceEdit","24/DeviceRotation","24/Ar","24/FrameSize","24/Body","24/BodyEdit","24/HairEdit","24/ItemRemove","24/AddModel","24/ChangeCharactor","24/Pose","24/Show","24/ShowOutline","24/Hide","24/Idea","24/Book","24/Message","24/Images","24/Image","24/Video","24/Hashtag","24/ImageAlt","24/Discovery","24/Description","24/File","24/Archive","24/Invoice","24/Announcement","24/Link","24/Manga","24/Novels","24/Contest","24/LatestWorks","24/Ranking","24/Feed","24/Groups","24/Events","24/Projects","24/Codes","24/Invalid","32/LikeOff","32/LikeOn","32/LikeOnPrivate","24/ImageHidden","Inline/Folder","Inline/Pencil","Inline/Comment","16/View","16/ImageResponse","16/Like","16/Smile","16/Comment","16/Ranking","16/Artwork","24/NoImage"] as const;
4
4
 
5
- // prettier-ignore
6
- export const KNOWN_ICON_FILES = ["32/ShareIos","32/Dot","32/Message","32/Gift","32/Close","32/Prev","32/Next","32/PullDown","32/PullUp","32/Expand","32/Collapse","32/Edit","32/ShareAndroid","32/BookmarkOff","32/BookmarkOn","32/NovelViewerSettings","32/CommentOff","32/CommentOn","32/Home","32/Index","32/Serif","32/Camera","32/Delete","32/Pan","32/RollVertical","32/RollHorizontal","32/ZoomIn","32/Upload","32/Shopping","32/User","32/HorizontalWriting","32/Notification","32/SansSerif","32/VerticalWriting","32/NotificationOff","24/Expand","24/Collapse","24/Close","24/Pencil","24/PencilDraw","24/PencilText","24/Reorder","24/Add","24/Subtract","24/Star","24/Check","24/Send","24/LockLock","24/LockUnlock","24/Up","24/Down","24/ReloadLoop","24/Reload","24/Options","24/CommentOutline","24/Trash","24/TrashAlt","24/ShareIos","24/ViewGrid3Columns","24/ViewGrid2Columns","24/ViewList","24/Upload","24/UploadAlt","24/DownloadAlt","24/ArrowUp","24/LikeOff","24/LikeOn","24/ArrowDown","24/OptionsAlt","24/PencilLive","24/OpenInNew","24/Settings","24/Roll","24/Calendar","24/Save","24/Logout","24/CommentFill","24/ShareAndroid","16/More","16/Back","16/Search","16/Add","16/Check","16/Menu","16/Dot","16/Book","16/ArrowDown","16/Speaker","16/Remove","16/BookmarkOn","16/BookmarkOff","16/Filter","16/Error","16/Info","16/Pencil","Inline/LikeOff","Inline/View","Inline/BookmarkOff","Inline/BookmarkOn","Inline/Breadcrumbs","Inline/More","Inline/ImageResponse","Inline/Lock","Inline/External","Inline/Location","Inline/View","Inline/Like","Inline/Smile","Inline/SmileOn","Inline/Check","Inline/List","Inline/ContextMenu","Inline/Add","Inline/Remove","Inline/Images","Inline/Nextworks","Inline/Filter","Inline/Users","24/Next","24/PullDown","24/PullUp","24/Prev","24/Dot","24/Notification","24/NotificationOff","24/Search","24/Menu","24/Index","24/Services","24/Error","24/Home","24/Warning","24/Info","24/QuestionOutline","24/ChatBot","24/ErrorOctagon","24/Question","24/Alart","24/DotAlt","24/ImageHidden","24/AddRubi","24/ImageReplace","24/Text","24/AddText","24/FormatAlignRight","24/FormatAlignLeft","24/FormatAlignCenter","24/AddImage","24/ImgContain","24/ImgCover","24/BringForward","24/BringBackward","24/RotateRight","24/Palette","24/Duplicate","24/FormatLineSpacing","24/FormatLetterSpacing","24/Rotate90DegreesCc","24/Rotate90DegreesC","24/FormatFontSize","24/FormatFontFamily","24/FormatColorFill","24/FormatColorFillNoColor","24/ChromaticAberration","24/Move1","24/Play","24/Pause","24/Microphone","24/PauseAlt","24/CameraVideo","24/Camera","24/Fov","24/Sun","24/Shutter","24/Contrast","24/FrameEffect","24/Hue","24/Saturation","24/Glow","24/Binet","24/Overlay","24/Flare","24/Dust","24/Temperature","24/Filter","24/Usagi","24/Set","24/Gift","24/MobilePhone","24/Users","24/AddPeople","24/Person","24/Emoji","24/Shopping","24/UsagiAlt","24/Fashion","24/FaceEdit","24/DeviceRotation","24/Ar","24/FrameSize","24/Body","24/BodyEdit","24/HairEdit","24/ItemRemove","24/AddModel","24/ChangeCharactor","24/Pose","24/Show","24/ShowOutline","24/Hide","24/Idea","24/Book","24/Message","24/Images","24/Image","24/Video","24/Hashtag","24/ImageAlt","24/Discovery","24/Description","24/File","24/Archive","24/Invoice","24/Announcement","24/Link","24/Manga","24/Novels","24/Contest","24/LatestWorks","24/Ranking","24/Feed","24/Groups","24/Events","24/Projects","24/Codes","24/Invalid","32/LikeOff","32/LikeOn","32/LikeOnPrivate","24/ImageHidden","Inline/Folder","Inline/Pencil","Inline/Comment"] as const;
7
-
8
- export type KnownIconFile = typeof KNOWN_ICON_FILES[number]
9
-
10
- /* eslint-enable */
5
+ export type KnownIconFile = typeof KNOWN_ICON_FILES[number];
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@ import { PixivIcon, Props } from './PixivIcon'
2
2
  import { __SERVER__ } from './ssr'
3
3
  export { PixivIcon, type KnownIconType, type Props } from './PixivIcon'
4
4
  export { KNOWN_ICON_FILES } from './filenames'
5
- export { PixivIconLoadError } from './loaders'
5
+ export { PixivIconLoadError } from './BaseLoader'
6
6
 
7
7
  declare global {
8
8
  interface Window {
package/src/loaders.ts CHANGED
@@ -1,57 +1,84 @@
1
- const cache = new Map<string, string>()
1
+ import { Loadable, BaseLoader } from './BaseLoader'
2
2
 
3
- interface SvgModule {
4
- // eslint-disable-next-line @typescript-eslint/naming-convention
5
- __esModule: true
6
- default: string
7
- }
3
+ /**
4
+ * オブジェクトプール。Loader のインスタンスは作り次第ここに入れる
5
+ *
6
+ * 同じアイコンへの複数回のリクエストが起きないためには、Loader がこの中でユニークでないと行けない
7
+ */
8
+ const pool = new Map<string, Loadable>()
9
+
10
+ /**
11
+ * アイコンを特定の URL から取得する Loader
12
+ */
13
+ export class UrlLoader implements Loadable {
14
+ private fetcher: BaseLoader
15
+
16
+ static find(name: string) {
17
+ return pool.get(name)
18
+ }
19
+
20
+ static register(name: string, url: string) {
21
+ const loader = new UrlLoader(name, url)
22
+
23
+ pool.set(name, loader)
24
+ }
25
+
26
+ private constructor(name: string, url: string) {
27
+ this.fetcher = new BaseLoader(name, () => Promise.resolve(url))
28
+ }
29
+
30
+ fetch(): Promise<string> {
31
+ return this.fetcher.fetch()
32
+ }
8
33
 
9
- export class PixivIconLoadError extends Error {
10
- constructor(message?: string) {
11
- super(message)
12
- Object.setPrototypeOf(this, new.target)
34
+ isLoading() {
35
+ return this.fetcher.isLoading()
13
36
  }
14
37
  }
15
38
 
16
- function createLoader(getPath: (attributeName: string) => Promise<string>) {
17
- return async function getSvgString(attributeName: string): Promise<string> {
18
- if (cache.has(attributeName)) {
19
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
20
- return cache.get(attributeName)!
39
+ /**
40
+ * アイコン名から import すべきファイル名( このパッケージ内にある )を解決してくる Loader
41
+ */
42
+ export class FileLoader implements Loadable {
43
+ private fetcher: BaseLoader
44
+
45
+ static findOrRegister(name: string) {
46
+ const registeredLoader = pool.get(name)
47
+ if (registeredLoader) {
48
+ return registeredLoader
21
49
  }
22
50
 
23
- const rawSvg = await getPath(attributeName)
24
- .then((path) => fetch(path))
25
- .then((response) => {
26
- if (!response.ok) {
27
- throw new PixivIconLoadError(
28
- `Failed to fetch <pixiv-icon name="${attributeName}">`
29
- )
30
- }
51
+ const newLoader = new FileLoader(name)
52
+ pool.set(name, newLoader)
31
53
 
32
- return response.text()
33
- })
54
+ return newLoader
55
+ }
34
56
 
35
- cache.set(attributeName, rawSvg)
57
+ private constructor(name: string) {
58
+ this.fetcher = new BaseLoader(name, () => getSourceFromName(name))
59
+ }
36
60
 
37
- return rawSvg
61
+ fetch(): Promise<string> {
62
+ return this.fetcher.fetch()
38
63
  }
39
- }
40
64
 
41
- export type Loader = ReturnType<typeof createLoader>
65
+ isLoading() {
66
+ return this.fetcher.isLoading()
67
+ }
68
+ }
42
69
 
43
- const loadFromRawUrl: Loader = createLoader((attributeName) =>
44
- Promise.resolve(attributeName)
45
- )
70
+ interface SvgModule {
71
+ // eslint-disable-next-line @typescript-eslint/naming-convention
72
+ __esModule: true
73
+ default: string
74
+ }
46
75
 
47
- const loadFromFile: Loader = createLoader(async (attributeName) => {
48
- const [size, name] = attributeName.split('/')
76
+ async function getSourceFromName(fullName: string) {
77
+ const [size, name] = fullName.split('/')
49
78
 
50
79
  const { default: filename } = (await import(
51
80
  `../svg/${encodeURIComponent(size)}/${encodeURIComponent(name)}.svg`
52
81
  )) as SvgModule
53
82
 
54
83
  return filename
55
- })
56
-
57
- export const loaders = { loadFromRawUrl, loadFromFile }
84
+ }
package/src/ssr.ts CHANGED
@@ -2,11 +2,8 @@ export const __SERVER__ = typeof window === 'undefined'
2
2
 
3
3
  const CAN_USE_DOM = typeof HTMLElement !== 'undefined'
4
4
 
5
- /**
6
- * NOTICE: SSR 環境では `extends HTMLElement` できない
7
- *
8
- * @see https://github.com/vuejs/core/blob/9c304bfe7942a20264235865b4bb5f6e53fdee0d/packages/runtime-dom/src/apiCustomElement.ts#L143-L145
9
- */
10
- export const BaseElement = (
11
- !__SERVER__ && CAN_USE_DOM ? HTMLElement : class {}
12
- ) as typeof HTMLElement
5
+ // Workaround: `extends HTMLELement` の形式でないとbabelのトランスパイルがおかしくなる
6
+ if (__SERVER__ || !CAN_USE_DOM) {
7
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
8
+ globalThis.HTMLElement = class {} as any
9
+ }
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 3a2 2 0 00-2 2v6a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2H4zm4 2H4v6h4V5z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M5.4 13.409l-2.64.377.333-2.332a6 6 0 112.306 1.955z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.793 3.293a1 1 0 011.414 0L13.914 8l-4.707 4.707a1 1 0 01-1.414-1.414L10.086 9H6a2 2 0 00-2 2v1a1 1 0 11-2 0v-1a4 4 0 014-4h4.086L7.793 4.707a1 1 0 010-1.414z" fill="currentColor"></path></svg>
package/svg/16/Info.svg CHANGED
@@ -1 +1 @@
1
- <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 16A8 8 0 118 0a8 8 0 010 16z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M14 8A6 6 0 112 8a6 6 0 0112 0zM8 6.25a1.25 1.25 0 100-2.5 1.25 1.25 0 000 2.5zm-1 1.5v3.5a1 1 0 102 0v-3.5a1 1 0 00-2 0z" fill="#fff"></path></svg>
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 8a8 8 0 1016 0A8 8 0 000 8zm8 6A6 6 0 108 2a6 6 0 000 12zm1.25-9a1.25 1.25 0 11-2.5 0 1.25 1.25 0 012.5 0zM7 11.25v-3.5a1 1 0 012 0v3.5a1 1 0 11-2 0z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M11 2.75a3 3 0 013 3c0 3.967-4.663 6.963-5.767 7.617a.452.452 0 01-.466 0C6.663 12.713 2 9.717 2 5.75a3 3 0 013-3c1.127 0 2.34.852 3 1.918.66-1.066 1.873-1.918 3-1.918z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M9.4 3.8a1.4 1.4 0 01-.716 1.222c.475.705 1.16 1.474 1.941 1.578.596.08 1.286-.222 1.85-.565a1.4 1.4 0 11.894.547l-.624 3.748a1.4 1.4 0 01-1.381 1.17H4.636a1.4 1.4 0 01-1.381-1.17L2.63 6.581a1.4 1.4 0 11.894-.547c.565.344 1.255.646 1.851.566.781-.104 1.466-.873 1.94-1.578A1.4 1.4 0 119.4 3.8zM4.5 12.2a.7.7 0 100 1.4h7a.7.7 0 100-1.4h-7z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M4 8a2 2 0 100-4 2 2 0 000 4zM12 8a2 2 0 100-4 2 2 0 000 4zM11.89 10.89a5.5 5.5 0 01-7.78 0 1 1 0 011.415-1.415 3.5 3.5 0 004.95 0 1 1 0 111.414 1.414z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M8 3C4 3 1 8 1 8s3 5 7 5 7-5 7-5-3-5-7-5zm0 7.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5z" fill="currentColor"></path></svg>
@@ -1,4 +1,3 @@
1
1
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" fill="#858585"/>
3
- <path fill-rule="evenodd" clip-rule="evenodd" d="M14 14L18 6L10 10L6 18L14 14ZM13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12C11 11.4477 11.4477 11 12 11C12.5523 11 13 11.4477 13 12Z" fill="white"/>
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12ZM6 18L10 10L18 6L14 14L6 18ZM12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" fill="#858585"/>
4
3
  </svg>
package/svg/24/Emoji.svg CHANGED
@@ -1 +1 @@
1
- <svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8 2h8a6 6 0 016 6v8a6 6 0 01-6 6H8a6 6 0 01-6-6V8a6 6 0 016-6z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M4 8a4 4 0 014-4h8a4 4 0 014 4v8a4 4 0 01-4 4H8a4 4 0 01-4-4V8zm6 2.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zm5.5 1.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm.39 3.89a5.5 5.5 0 01-7.78 0 1 1 0 111.415-1.415 3.5 3.5 0 004.95 0 1 1 0 111.414 1.414z" fill="#fff"></path></svg>
1
+ <svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="M16 2H8a6 6 0 00-6 6v8a6 6 0 006 6h8a6 6 0 006-6V8a6 6 0 00-6-6zM8 4a4 4 0 00-4 4v8a4 4 0 004 4h8a4 4 0 004-4V8a4 4 0 00-4-4H8zm.5 8a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm8.5-1.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zm-8.89 5.39a5.5 5.5 0 007.78 0 1 1 0 00-1.415-1.415 3.5 3.5 0 01-4.95 0 1 1 0 10-1.414 1.414z" fill="currentColor"></path></svg>
@@ -1 +1 @@
1
- <svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path clip-rule="evenodd" d="M12 6.346C13.035 4.956 14.568 4 16.09 4A4.91 4.91 0 0121 8.91c0 4.38-2.845 8.131-8.4 11.239a1.227 1.227 0 01-1.2 0C5.846 17.04 3 13.289 3 8.909A4.91 4.91 0 017.91 4c1.522 0 3.055.955 4.09 2.346z" stroke="currentColor" stroke-width="2"></path></svg>
1
+ <svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 6.346C13.035 4.956 14.568 4 16.09 4A4.91 4.91 0 0121 8.91c0 4.38-2.845 8.131-8.4 11.239a1.227 1.227 0 01-1.2 0C5.846 17.04 3 13.289 3 8.909A4.91 4.91 0 017.91 4c1.522 0 3.055.955 4.09 2.346z" fill="#fff" stroke="currentColor" stroke-width="2"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 4.5A2.5 2.5 0 003.5 7v10A2.5 2.5 0 006 19.5h12a2.5 2.5 0 002.5-2.5V7A2.5 2.5 0 0018 4.5H6zM2.5 7A3.5 3.5 0 016 3.5h12A3.5 3.5 0 0121.5 7v10a3.5 3.5 0 01-3.5 3.5H6A3.5 3.5 0 012.5 17V7zm11 2a2.5 2.5 0 115 0 2.5 2.5 0 01-5 0zM16 7.5a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm-7 3.441l6 3 4.5-2.25V17a1.5 1.5 0 01-1.5 1.5H6A1.5 1.5 0 014.5 17v-3.809l4.5-2.25zm-3.5 2.868V17a.5.5 0 00.5.5h12a.5.5 0 00.5-.5v-3.691l-3.5 1.75-6-3-3.5 1.75z" fill="currentColor"></path></svg>
@@ -0,0 +1 @@
1
+ <svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.707 9.707a1 1 0 00-1.414-1.414l-3 3a.998.998 0 000 1.414l3 3a1 1 0 001.414-1.414L9.414 13H17a1 1 0 100-2H9.414l1.293-1.293z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M17 3H7a4 4 0 00-4 4v10a4 4 0 004 4h10a4 4 0 004-4V7a4 4 0 00-4-4zM5 7a2 2 0 012-2h10a2 2 0 012 2v10a2 2 0 01-2 2H7a2 2 0 01-2-2V7z" fill="currentColor"></path></svg>