@charcoal-ui/icons 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PixivIcon.d.ts +5 -1
- package/dist/PixivIcon.d.ts.map +1 -1
- package/dist/index.cjs.js +47 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +47 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/loaders/CharcoalIconFilesLoader.d.ts +1 -1
- package/dist/loaders/CharcoalIconFilesLoader.d.ts.map +1 -1
- package/dist/loaders/CustomRawFileLoader.d.ts +15 -0
- package/dist/loaders/CustomRawFileLoader.d.ts.map +1 -0
- package/dist/loaders/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/16/TestIconThatNeverExists.d.ts +2 -0
- package/src/16/TestIconThatNeverExists.js +1 -0
- package/src/CustomIcon.mdx +52 -2
- package/src/PixivIcon.story.tsx +29 -1
- package/src/PixivIcon.ts +15 -4
- package/src/loaders/CharcoalIconFilesLoader.ts +1 -1
- package/src/loaders/CustomRawFileLoader.ts +27 -0
- package/src/loaders/index.ts +21 -5
- package/dist/PixivIcon.story.d.ts +0 -8
- package/dist/PixivIcon.story.d.ts.map +0 -1
package/dist/PixivIcon.d.ts
CHANGED
|
@@ -14,8 +14,12 @@ export declare class PixivIcon extends HTMLElement {
|
|
|
14
14
|
static readonly tagName = "pixiv-icon";
|
|
15
15
|
/**
|
|
16
16
|
* NOTE: icon content should be sanitized before pass to extend()
|
|
17
|
+
*
|
|
18
|
+
* XSSに注意すること。
|
|
19
|
+
* 登録したファイルの中身が直接domに反映されるため、XSSに繋がる可能性があります。
|
|
20
|
+
* 信用していないソースからアイコンを追加する場合dom-purifyなどを経由してください。
|
|
17
21
|
*/
|
|
18
|
-
static extend(map: Extended extends true ? Record<ExtendedIconFile, string> : Record<string, string>): void;
|
|
22
|
+
static extend(map: Extended extends true ? Record<ExtendedIconFile, string | (() => Promise<string>)> : Record<string, string | (() => Promise<string>)>): void;
|
|
19
23
|
static get observedAttributes(): readonly ["name", "scale", "unsafe-non-guideline-scale"];
|
|
20
24
|
private svgContent?;
|
|
21
25
|
private observer?;
|
package/dist/PixivIcon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PixivIcon.d.ts","sourceRoot":"","sources":["../src/PixivIcon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"PixivIcon.d.ts","sourceRoot":"","sources":["../src/PixivIcon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AASnD,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC;CAAG;AAExE,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,KAAK,CAAC,iBAAiB,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,EACvE,WAAW,GAAG,KAAK,CACpB;IACD,IAAI,EAAE,MAAM,aAAa,CAAA;IACzB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;IACnC,4BAA4B,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAI9C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,KAAK,gBAAgB,GAAG,OAAO,CAAC,MAAM,aAAa,EAAE,aAAa,CAAC,CAAA;AACnE,KAAK,QAAQ,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAAC,KAAK,CAAC,GAC9C,KAAK,GACL,IAAI,CAAA;AAER,qBAAa,SAAU,SAAQ,WAAW;IACxC,MAAM,CAAC,QAAQ,CAAC,OAAO,gBAAe;IAEtC;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,QAAQ,SAAS,IAAI,GACtB,MAAM,CAAC,gBAAgB,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAC1D,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAwBtD,MAAM,KAAK,kBAAkB,6DAE5B;IAED,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAsB;IACvC,OAAO,CAAC,SAAS,CAAQ;IAEzB,IAAI,KAAK;;;;MAqBR;IAED,IAAI,gBAAgB,kBAiBnB;IAED,IAAI,UAAU,WA0Bb;;IAOK,iBAAiB;IAOvB,oBAAoB;IAMpB,wBAAwB,CACtB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,MAAM;IAuBlB,MAAM;YA6BQ,OAAO;IAKrB,OAAO,CAAC,gBAAgB;CAmBzB"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -104,6 +104,27 @@ var CharcoalIconFilesLoader = class {
|
|
|
104
104
|
}
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
+
// src/loaders/CustomRawFileLoader.ts
|
|
108
|
+
var _CustomRawFileLoader = class extends CharcoalIconFilesLoader {
|
|
109
|
+
get importIconFile() {
|
|
110
|
+
const icon = _CustomRawFileLoader.filePackages.get(this._name);
|
|
111
|
+
if (icon !== void 0)
|
|
112
|
+
return icon;
|
|
113
|
+
throw new Error("Custom icon file was not found");
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var CustomRawFileLoader = _CustomRawFileLoader;
|
|
117
|
+
/**
|
|
118
|
+
* icons-filesと同じ型のアイコンをしまっとくところ
|
|
119
|
+
*/
|
|
120
|
+
__publicField(CustomRawFileLoader, "filePackages", /* @__PURE__ */ new Map());
|
|
121
|
+
function addRawFile(name, importFn) {
|
|
122
|
+
CustomRawFileLoader.filePackages.set(name, importFn);
|
|
123
|
+
}
|
|
124
|
+
function isKnownRawIconFile(name) {
|
|
125
|
+
return CustomRawFileLoader.filePackages.has(name);
|
|
126
|
+
}
|
|
127
|
+
|
|
107
128
|
// src/loaders/CustomIconLoader.ts
|
|
108
129
|
var CustomIconLoader = class {
|
|
109
130
|
_name;
|
|
@@ -151,18 +172,31 @@ async function getIcon(name) {
|
|
|
151
172
|
if (loader == null) {
|
|
152
173
|
throw new PixivIconLoadError(name, "Loader was not found");
|
|
153
174
|
}
|
|
154
|
-
|
|
175
|
+
try {
|
|
176
|
+
const svg = await loader.fetch();
|
|
177
|
+
if (typeof svg !== "string") {
|
|
178
|
+
console.warn(
|
|
179
|
+
`${name}: Expected load result to be a string, but received an unexpected type.`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return svg;
|
|
183
|
+
} catch (e) {
|
|
155
184
|
if (e instanceof PixivIconLoadError) {
|
|
156
185
|
throw e;
|
|
157
186
|
}
|
|
158
187
|
throw new PixivIconLoadError(name, e);
|
|
159
|
-
}
|
|
188
|
+
}
|
|
160
189
|
}
|
|
161
190
|
function resolveIconLoader(name) {
|
|
162
191
|
const registeredLoader = loaders.get(name);
|
|
163
192
|
if (registeredLoader) {
|
|
164
193
|
return registeredLoader;
|
|
165
194
|
}
|
|
195
|
+
if (isKnownRawIconFile(name)) {
|
|
196
|
+
const customFilePackageLoader = new CustomRawFileLoader(name);
|
|
197
|
+
loaders.set(name, customFilePackageLoader);
|
|
198
|
+
return customFilePackageLoader;
|
|
199
|
+
}
|
|
166
200
|
if (isKnownIconFile(name)) {
|
|
167
201
|
const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name);
|
|
168
202
|
loaders.set(name, charcoalIconFilesLoader);
|
|
@@ -185,19 +219,28 @@ var ROOT_MARGIN = 50;
|
|
|
185
219
|
var PixivIcon = class extends HTMLElement {
|
|
186
220
|
/**
|
|
187
221
|
* NOTE: icon content should be sanitized before pass to extend()
|
|
222
|
+
*
|
|
223
|
+
* XSSに注意すること。
|
|
224
|
+
* 登録したファイルの中身が直接domに反映されるため、XSSに繋がる可能性があります。
|
|
225
|
+
* 信用していないソースからアイコンを追加する場合dom-purifyなどを経由してください。
|
|
188
226
|
*/
|
|
189
227
|
static extend(map) {
|
|
190
228
|
(0, import_warning.default)(!__SERVER__, "Using `PixivIcon.extend()` on server has no effect");
|
|
191
229
|
if (__SERVER__) {
|
|
192
230
|
return;
|
|
193
231
|
}
|
|
194
|
-
Object.entries(map).forEach(([name,
|
|
232
|
+
Object.entries(map).forEach(([name, filePathOrUrlOrImportFn]) => {
|
|
195
233
|
if (!name.includes("/")) {
|
|
196
234
|
throw new TypeError(
|
|
197
235
|
`${name} is not a valid icon name. "name" must be named like [size]/[Name].`
|
|
198
236
|
);
|
|
199
237
|
}
|
|
200
|
-
|
|
238
|
+
if (typeof filePathOrUrlOrImportFn === "string") {
|
|
239
|
+
addCustomIcon(name, filePathOrUrlOrImportFn);
|
|
240
|
+
}
|
|
241
|
+
if (typeof filePathOrUrlOrImportFn === "function") {
|
|
242
|
+
addRawFile(name, filePathOrUrlOrImportFn);
|
|
243
|
+
}
|
|
201
244
|
});
|
|
202
245
|
}
|
|
203
246
|
static get observedAttributes() {
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/PixivIcon.ts","../src/charcoalIconFiles.ts","../src/loaders/PixivIconLoadError.ts","../src/loaders/CharcoalIconFilesLoader.ts","../src/loaders/CustomIconLoader.ts","../src/loaders/index.ts","../src/ssr.ts"],"sourcesContent":["import { PixivIcon } from './PixivIcon'\nimport { __SERVER__ } from './ssr'\nexport { PixivIcon, type KnownIconType, type Props } from './PixivIcon'\nexport { KNOWN_ICON_FILES } from './charcoalIconFiles'\nexport { PixivIconLoadError } from './loaders/PixivIconLoadError'\n\ndeclare global {\n interface Window {\n PixivIcon: typeof PixivIcon\n }\n}\n\ndeclare module 'react' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'pixiv-icon': import('./PixivIcon').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","import type React from 'react'\nimport warning from 'warning'\nimport { KnownIconFile } from './charcoalIconFiles'\nimport { getIcon, addCustomIcon } from './loaders'\nimport { __SERVER__ } from './ssr'\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 /**\n * NOTE: icon content should be sanitized before pass to extend()\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, filePathOrUrl]) => {\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 addCustomIcon(name, filePathOrUrl)\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 if (!Number.isFinite(size)) {\n throw new TypeError(`icon size must not be NaN`)\n }\n\n const style = `<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\n const svg =\n this.svgContent !== undefined\n ? this.svgContent\n : `<svg viewBox=\"0 0 ${size} ${size}\"></svg>`\n\n // NOTE: User should sanitize the svg content before passing to charcoal.\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 this.svgContent = await getIcon(name)\n this.render()\n }\n\n private waitUntilVisible() {\n return new Promise<void>((resolve) => {\n this.observer = new IntersectionObserver(\n (entries) => {\n // In Chromium based browsers, multiple entries can be returned even only observe once.\n // Here, we don't care about the entry time but only if isIntersecting happened.\n const isIntersecting = entries.some((entry) => entry.isIntersecting)\n if (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","import charcoalIconFiles from '@charcoal-ui/icon-files'\n\nexport default charcoalIconFiles\nexport type KnownIconFile = keyof typeof charcoalIconFiles\nexport const KNOWN_ICON_FILES = Object.keys(\n charcoalIconFiles\n) as KnownIconFile[]\n\nexport function isKnownIconFile(name: string): name is KnownIconFile {\n return name in charcoalIconFiles\n}\n","export class PixivIconLoadError extends Error {\n constructor(name: string, cause: unknown) {\n const message = formatMessage(name, cause)\n\n super(message, { cause })\n this.name = 'PixivIconLoadError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nfunction formatMessage(name: string, cause: unknown) {\n const message = `Failed to fetch <pixiv-icon name=\"${name}\">`\n if (cause == null) {\n return message\n }\n\n if (cause instanceof Error) {\n return `${message}: ${cause.toString()})`\n }\n\n return `${message}: ${JSON.stringify(cause)})`\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\nimport charcoalIconFiles, { KnownIconFile } from '../charcoalIconFiles'\n\n/**\n * `@charcoal-ui/icon-files` に収録されているアイコンを取ってくる\n */\nexport class CharcoalIconFilesLoader implements Loadable {\n private _name: KnownIconFile\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: KnownIconFile) {\n this._name = name\n }\n\n get importIconFile() {\n return charcoalIconFiles[this._name]\n }\n\n async fetch(): Promise<string> {\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.importIconFile()\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\n\n/**\n * `PixivIcon.extend()` で登録されたカスタムのアイコンを取得する\n */\nexport class CustomIconLoader implements Loadable {\n private _name: string\n private _filePathOrUrl: string\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: string, filePathOrUrl: string) {\n this._name = name\n this._filePathOrUrl = filePathOrUrl\n }\n\n async fetch(): Promise<string> {\n if (this._resultSvg !== undefined) {\n return this._resultSvg\n }\n\n if (this._promise) {\n return this._promise\n }\n\n this._promise = fetch(this._filePathOrUrl)\n .then((response) => {\n if (!response.ok) {\n throw new PixivIconLoadError(this._name, 'Response is not ok')\n }\n\n return response.text()\n })\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { isKnownIconFile } from '../charcoalIconFiles'\nimport { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'\nimport { CustomIconLoader } from './CustomIconLoader'\nimport { Loadable } from './Loadable'\nimport { PixivIconLoadError } from './PixivIconLoadError'\n\n/**\n * icon をロードするオブジェクトのプール。Loader のインスタンスは作り次第ここに入れる\n *\n * 同じアイコンへの複数回のリクエストが起きないためには、Loader がこの中でユニークでないと行けない\n */\nconst loaders = new Map<string, Loadable>()\n\nexport function addCustomIcon(name: string, filePathOrUrl: string) {\n loaders.set(name, new CustomIconLoader(name, filePathOrUrl))\n}\n\nexport async function getIcon(name: string) {\n const loader = resolveIconLoader(name)\n if (loader == null) {\n throw new PixivIconLoadError(name, 'Loader was not found')\n }\n\n return loader.fetch().catch((e) => {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n\n throw new PixivIconLoadError(name, e)\n })\n}\n\nfunction resolveIconLoader(name: string) {\n // 登録済み or キャッシュ済みの場合\n // addCustomIcon で登録された CustomIconLoader は常にこっちを通る\n const registeredLoader = loaders.get(name)\n if (registeredLoader) {\n return registeredLoader\n }\n\n // `@charcoal-ui/icon-files` に収録されているアイコンにはこれを返す\n if (isKnownIconFile(name)) {\n const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name)\n\n loaders.set(name, charcoalIconFilesLoader)\n return charcoalIconFilesLoader\n }\n\n // 存在しないアイコンにはこれを返す\n return null\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-explicit-any\n globalThis.HTMLElement = class {} as any\n}\n"],"mappings":"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","names":["charcoalIconFiles","warning"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PixivIcon.ts","../src/charcoalIconFiles.ts","../src/loaders/PixivIconLoadError.ts","../src/loaders/CharcoalIconFilesLoader.ts","../src/loaders/CustomRawFileLoader.ts","../src/loaders/CustomIconLoader.ts","../src/loaders/index.ts","../src/ssr.ts"],"sourcesContent":["import { PixivIcon } from './PixivIcon'\nimport { __SERVER__ } from './ssr'\nexport { PixivIcon, type KnownIconType, type Props } from './PixivIcon'\nexport { KNOWN_ICON_FILES } from './charcoalIconFiles'\nexport { PixivIconLoadError } from './loaders/PixivIconLoadError'\n\ndeclare global {\n interface Window {\n PixivIcon: typeof PixivIcon\n }\n}\n\ndeclare module 'react' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'pixiv-icon': import('./PixivIcon').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","import type React from 'react'\nimport warning from 'warning'\nimport { KnownIconFile } from './charcoalIconFiles'\nimport { getIcon, addCustomIcon } from './loaders'\nimport { addRawFile } from './loaders/CustomRawFileLoader'\nimport { __SERVER__ } from './ssr'\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 /**\n * NOTE: icon content should be sanitized before pass to extend()\n *\n * XSSに注意すること。\n * 登録したファイルの中身が直接domに反映されるため、XSSに繋がる可能性があります。\n * 信用していないソースからアイコンを追加する場合dom-purifyなどを経由してください。\n */\n static extend(\n map: Extended extends true\n ? Record<ExtendedIconFile, string | (() => Promise<string>)>\n : Record<string, string | (() => Promise<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, filePathOrUrlOrImportFn]) => {\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 if (typeof filePathOrUrlOrImportFn === 'string') {\n addCustomIcon(name, filePathOrUrlOrImportFn)\n }\n\n if (typeof filePathOrUrlOrImportFn === 'function') {\n addRawFile(name, filePathOrUrlOrImportFn)\n }\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 if (!Number.isFinite(size)) {\n throw new TypeError(`icon size must not be NaN`)\n }\n\n const style = `<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\n const svg =\n this.svgContent !== undefined\n ? this.svgContent\n : `<svg viewBox=\"0 0 ${size} ${size}\"></svg>`\n\n // NOTE: User should sanitize the svg content before passing to charcoal.\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 this.svgContent = await getIcon(name)\n this.render()\n }\n\n private waitUntilVisible() {\n return new Promise<void>((resolve) => {\n this.observer = new IntersectionObserver(\n (entries) => {\n // In Chromium based browsers, multiple entries can be returned even only observe once.\n // Here, we don't care about the entry time but only if isIntersecting happened.\n const isIntersecting = entries.some((entry) => entry.isIntersecting)\n if (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","import charcoalIconFiles from '@charcoal-ui/icon-files'\n\nexport default charcoalIconFiles\nexport type KnownIconFile = keyof typeof charcoalIconFiles\nexport const KNOWN_ICON_FILES = Object.keys(\n charcoalIconFiles\n) as KnownIconFile[]\n\nexport function isKnownIconFile(name: string): name is KnownIconFile {\n return name in charcoalIconFiles\n}\n","export class PixivIconLoadError extends Error {\n constructor(name: string, cause: unknown) {\n const message = formatMessage(name, cause)\n\n super(message, { cause })\n this.name = 'PixivIconLoadError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nfunction formatMessage(name: string, cause: unknown) {\n const message = `Failed to fetch <pixiv-icon name=\"${name}\">`\n if (cause == null) {\n return message\n }\n\n if (cause instanceof Error) {\n return `${message}: ${cause.toString()})`\n }\n\n return `${message}: ${JSON.stringify(cause)})`\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\nimport charcoalIconFiles, { KnownIconFile } from '../charcoalIconFiles'\n\n/**\n * `@charcoal-ui/icon-files` に収録されているアイコンを取ってくる\n */\nexport class CharcoalIconFilesLoader implements Loadable {\n protected _name: KnownIconFile\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: KnownIconFile) {\n this._name = name\n }\n\n get importIconFile() {\n return charcoalIconFiles[this._name]\n }\n\n async fetch(): Promise<string> {\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.importIconFile()\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { KnownIconFile } from '../charcoalIconFiles'\nimport { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'\n\nexport class CustomRawFileLoader extends CharcoalIconFilesLoader {\n /**\n * icons-filesと同じ型のアイコンをしまっとくところ\n */\n static filePackages = new Map<string, () => Promise<string>>()\n\n get importIconFile() {\n const icon = CustomRawFileLoader.filePackages.get(this._name)\n if (icon !== undefined) return icon\n\n throw new Error('Custom icon file was not found')\n }\n}\n\nexport function addRawFile(name: string, importFn: () => Promise<string>) {\n CustomRawFileLoader.filePackages.set(name, importFn)\n}\n\n/**\n * 登録されているfile packagesにiconがあればtrue\n */\nexport function isKnownRawIconFile(name: string): name is KnownIconFile {\n return CustomRawFileLoader.filePackages.has(name)\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\n\n/**\n * `PixivIcon.extend()` で登録されたカスタムのアイコンを取得する\n */\nexport class CustomIconLoader implements Loadable {\n private _name: string\n private _filePathOrUrl: string\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: string, filePathOrUrl: string) {\n this._name = name\n this._filePathOrUrl = filePathOrUrl\n }\n\n async fetch(): Promise<string> {\n if (this._resultSvg !== undefined) {\n return this._resultSvg\n }\n\n if (this._promise) {\n return this._promise\n }\n\n this._promise = fetch(this._filePathOrUrl)\n .then((response) => {\n if (!response.ok) {\n throw new PixivIconLoadError(this._name, 'Response is not ok')\n }\n\n return response.text()\n })\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { isKnownIconFile } from '../charcoalIconFiles'\nimport { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'\nimport { CustomRawFileLoader, isKnownRawIconFile } from './CustomRawFileLoader'\nimport { CustomIconLoader } from './CustomIconLoader'\nimport { Loadable } from './Loadable'\nimport { PixivIconLoadError } from './PixivIconLoadError'\n\n/**\n * icon をロードするオブジェクトのプール。Loader のインスタンスは作り次第ここに入れる\n *\n * 同じアイコンへの複数回のリクエストが起きないためには、Loader がこの中でユニークでないと行けない\n */\nconst loaders = new Map<string, Loadable>()\n\nexport function addCustomIcon(name: string, filePathOrUrl: string) {\n loaders.set(name, new CustomIconLoader(name, filePathOrUrl))\n}\n\nexport async function getIcon(name: string) {\n const loader = resolveIconLoader(name)\n if (loader == null) {\n throw new PixivIconLoadError(name, 'Loader was not found')\n }\n\n try {\n const svg = await loader.fetch()\n if (typeof svg !== 'string') {\n // eslint-disable-next-line no-console\n console.warn(\n `${name}: Expected load result to be a string, but received an unexpected type.`\n )\n }\n\n return svg\n } catch (e) {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n throw new PixivIconLoadError(name, e)\n }\n}\n\nfunction resolveIconLoader(name: string) {\n // 登録済み or キャッシュ済みの場合\n // addCustomIcon で登録された CustomIconLoader は常にこっちを通る\n const registeredLoader = loaders.get(name)\n if (registeredLoader) {\n return registeredLoader\n }\n\n // addRawFile で登録されたもの\n if (isKnownRawIconFile(name)) {\n const customFilePackageLoader = new CustomRawFileLoader(name)\n loaders.set(name, customFilePackageLoader)\n return customFilePackageLoader\n }\n\n if (isKnownIconFile(name)) {\n // `@charcoal-ui/icon-files` に収録されているアイコンにはこれを返す\n const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name)\n loaders.set(name, charcoalIconFilesLoader)\n return charcoalIconFilesLoader\n }\n\n // 存在しないアイコンにはこれを返す\n return null\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-explicit-any\n globalThis.HTMLElement = class {} as any\n}\n"],"mappings":"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","names":["charcoalIconFiles","warning"]}
|
package/dist/index.esm.js
CHANGED
|
@@ -68,6 +68,27 @@ var CharcoalIconFilesLoader = class {
|
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
// src/loaders/CustomRawFileLoader.ts
|
|
72
|
+
var _CustomRawFileLoader = class extends CharcoalIconFilesLoader {
|
|
73
|
+
get importIconFile() {
|
|
74
|
+
const icon = _CustomRawFileLoader.filePackages.get(this._name);
|
|
75
|
+
if (icon !== void 0)
|
|
76
|
+
return icon;
|
|
77
|
+
throw new Error("Custom icon file was not found");
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var CustomRawFileLoader = _CustomRawFileLoader;
|
|
81
|
+
/**
|
|
82
|
+
* icons-filesと同じ型のアイコンをしまっとくところ
|
|
83
|
+
*/
|
|
84
|
+
__publicField(CustomRawFileLoader, "filePackages", /* @__PURE__ */ new Map());
|
|
85
|
+
function addRawFile(name, importFn) {
|
|
86
|
+
CustomRawFileLoader.filePackages.set(name, importFn);
|
|
87
|
+
}
|
|
88
|
+
function isKnownRawIconFile(name) {
|
|
89
|
+
return CustomRawFileLoader.filePackages.has(name);
|
|
90
|
+
}
|
|
91
|
+
|
|
71
92
|
// src/loaders/CustomIconLoader.ts
|
|
72
93
|
var CustomIconLoader = class {
|
|
73
94
|
_name;
|
|
@@ -115,18 +136,31 @@ async function getIcon(name) {
|
|
|
115
136
|
if (loader == null) {
|
|
116
137
|
throw new PixivIconLoadError(name, "Loader was not found");
|
|
117
138
|
}
|
|
118
|
-
|
|
139
|
+
try {
|
|
140
|
+
const svg = await loader.fetch();
|
|
141
|
+
if (typeof svg !== "string") {
|
|
142
|
+
console.warn(
|
|
143
|
+
`${name}: Expected load result to be a string, but received an unexpected type.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return svg;
|
|
147
|
+
} catch (e) {
|
|
119
148
|
if (e instanceof PixivIconLoadError) {
|
|
120
149
|
throw e;
|
|
121
150
|
}
|
|
122
151
|
throw new PixivIconLoadError(name, e);
|
|
123
|
-
}
|
|
152
|
+
}
|
|
124
153
|
}
|
|
125
154
|
function resolveIconLoader(name) {
|
|
126
155
|
const registeredLoader = loaders.get(name);
|
|
127
156
|
if (registeredLoader) {
|
|
128
157
|
return registeredLoader;
|
|
129
158
|
}
|
|
159
|
+
if (isKnownRawIconFile(name)) {
|
|
160
|
+
const customFilePackageLoader = new CustomRawFileLoader(name);
|
|
161
|
+
loaders.set(name, customFilePackageLoader);
|
|
162
|
+
return customFilePackageLoader;
|
|
163
|
+
}
|
|
130
164
|
if (isKnownIconFile(name)) {
|
|
131
165
|
const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name);
|
|
132
166
|
loaders.set(name, charcoalIconFilesLoader);
|
|
@@ -149,19 +183,28 @@ var ROOT_MARGIN = 50;
|
|
|
149
183
|
var PixivIcon = class extends HTMLElement {
|
|
150
184
|
/**
|
|
151
185
|
* NOTE: icon content should be sanitized before pass to extend()
|
|
186
|
+
*
|
|
187
|
+
* XSSに注意すること。
|
|
188
|
+
* 登録したファイルの中身が直接domに反映されるため、XSSに繋がる可能性があります。
|
|
189
|
+
* 信用していないソースからアイコンを追加する場合dom-purifyなどを経由してください。
|
|
152
190
|
*/
|
|
153
191
|
static extend(map) {
|
|
154
192
|
warning(!__SERVER__, "Using `PixivIcon.extend()` on server has no effect");
|
|
155
193
|
if (__SERVER__) {
|
|
156
194
|
return;
|
|
157
195
|
}
|
|
158
|
-
Object.entries(map).forEach(([name,
|
|
196
|
+
Object.entries(map).forEach(([name, filePathOrUrlOrImportFn]) => {
|
|
159
197
|
if (!name.includes("/")) {
|
|
160
198
|
throw new TypeError(
|
|
161
199
|
`${name} is not a valid icon name. "name" must be named like [size]/[Name].`
|
|
162
200
|
);
|
|
163
201
|
}
|
|
164
|
-
|
|
202
|
+
if (typeof filePathOrUrlOrImportFn === "string") {
|
|
203
|
+
addCustomIcon(name, filePathOrUrlOrImportFn);
|
|
204
|
+
}
|
|
205
|
+
if (typeof filePathOrUrlOrImportFn === "function") {
|
|
206
|
+
addRawFile(name, filePathOrUrlOrImportFn);
|
|
207
|
+
}
|
|
165
208
|
});
|
|
166
209
|
}
|
|
167
210
|
static get observedAttributes() {
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/PixivIcon.ts","../src/charcoalIconFiles.ts","../src/loaders/PixivIconLoadError.ts","../src/loaders/CharcoalIconFilesLoader.ts","../src/loaders/CustomIconLoader.ts","../src/loaders/index.ts","../src/ssr.ts","../src/index.ts"],"sourcesContent":["import type React from 'react'\nimport warning from 'warning'\nimport { KnownIconFile } from './charcoalIconFiles'\nimport { getIcon, addCustomIcon } from './loaders'\nimport { __SERVER__ } from './ssr'\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 /**\n * NOTE: icon content should be sanitized before pass to extend()\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, filePathOrUrl]) => {\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 addCustomIcon(name, filePathOrUrl)\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 if (!Number.isFinite(size)) {\n throw new TypeError(`icon size must not be NaN`)\n }\n\n const style = `<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\n const svg =\n this.svgContent !== undefined\n ? this.svgContent\n : `<svg viewBox=\"0 0 ${size} ${size}\"></svg>`\n\n // NOTE: User should sanitize the svg content before passing to charcoal.\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 this.svgContent = await getIcon(name)\n this.render()\n }\n\n private waitUntilVisible() {\n return new Promise<void>((resolve) => {\n this.observer = new IntersectionObserver(\n (entries) => {\n // In Chromium based browsers, multiple entries can be returned even only observe once.\n // Here, we don't care about the entry time but only if isIntersecting happened.\n const isIntersecting = entries.some((entry) => entry.isIntersecting)\n if (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","import charcoalIconFiles from '@charcoal-ui/icon-files'\n\nexport default charcoalIconFiles\nexport type KnownIconFile = keyof typeof charcoalIconFiles\nexport const KNOWN_ICON_FILES = Object.keys(\n charcoalIconFiles\n) as KnownIconFile[]\n\nexport function isKnownIconFile(name: string): name is KnownIconFile {\n return name in charcoalIconFiles\n}\n","export class PixivIconLoadError extends Error {\n constructor(name: string, cause: unknown) {\n const message = formatMessage(name, cause)\n\n super(message, { cause })\n this.name = 'PixivIconLoadError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nfunction formatMessage(name: string, cause: unknown) {\n const message = `Failed to fetch <pixiv-icon name=\"${name}\">`\n if (cause == null) {\n return message\n }\n\n if (cause instanceof Error) {\n return `${message}: ${cause.toString()})`\n }\n\n return `${message}: ${JSON.stringify(cause)})`\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\nimport charcoalIconFiles, { KnownIconFile } from '../charcoalIconFiles'\n\n/**\n * `@charcoal-ui/icon-files` に収録されているアイコンを取ってくる\n */\nexport class CharcoalIconFilesLoader implements Loadable {\n private _name: KnownIconFile\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: KnownIconFile) {\n this._name = name\n }\n\n get importIconFile() {\n return charcoalIconFiles[this._name]\n }\n\n async fetch(): Promise<string> {\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.importIconFile()\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\n\n/**\n * `PixivIcon.extend()` で登録されたカスタムのアイコンを取得する\n */\nexport class CustomIconLoader implements Loadable {\n private _name: string\n private _filePathOrUrl: string\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: string, filePathOrUrl: string) {\n this._name = name\n this._filePathOrUrl = filePathOrUrl\n }\n\n async fetch(): Promise<string> {\n if (this._resultSvg !== undefined) {\n return this._resultSvg\n }\n\n if (this._promise) {\n return this._promise\n }\n\n this._promise = fetch(this._filePathOrUrl)\n .then((response) => {\n if (!response.ok) {\n throw new PixivIconLoadError(this._name, 'Response is not ok')\n }\n\n return response.text()\n })\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { isKnownIconFile } from '../charcoalIconFiles'\nimport { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'\nimport { CustomIconLoader } from './CustomIconLoader'\nimport { Loadable } from './Loadable'\nimport { PixivIconLoadError } from './PixivIconLoadError'\n\n/**\n * icon をロードするオブジェクトのプール。Loader のインスタンスは作り次第ここに入れる\n *\n * 同じアイコンへの複数回のリクエストが起きないためには、Loader がこの中でユニークでないと行けない\n */\nconst loaders = new Map<string, Loadable>()\n\nexport function addCustomIcon(name: string, filePathOrUrl: string) {\n loaders.set(name, new CustomIconLoader(name, filePathOrUrl))\n}\n\nexport async function getIcon(name: string) {\n const loader = resolveIconLoader(name)\n if (loader == null) {\n throw new PixivIconLoadError(name, 'Loader was not found')\n }\n\n return loader.fetch().catch((e) => {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n\n throw new PixivIconLoadError(name, e)\n })\n}\n\nfunction resolveIconLoader(name: string) {\n // 登録済み or キャッシュ済みの場合\n // addCustomIcon で登録された CustomIconLoader は常にこっちを通る\n const registeredLoader = loaders.get(name)\n if (registeredLoader) {\n return registeredLoader\n }\n\n // `@charcoal-ui/icon-files` に収録されているアイコンにはこれを返す\n if (isKnownIconFile(name)) {\n const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name)\n\n loaders.set(name, charcoalIconFilesLoader)\n return charcoalIconFilesLoader\n }\n\n // 存在しないアイコンにはこれを返す\n return null\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-explicit-any\n globalThis.HTMLElement = class {} as any\n}\n","import { PixivIcon } from './PixivIcon'\nimport { __SERVER__ } from './ssr'\nexport { PixivIcon, type KnownIconType, type Props } from './PixivIcon'\nexport { KNOWN_ICON_FILES } from './charcoalIconFiles'\nexport { PixivIconLoadError } from './loaders/PixivIconLoadError'\n\ndeclare global {\n interface Window {\n PixivIcon: typeof PixivIcon\n }\n}\n\ndeclare module 'react' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'pixiv-icon': import('./PixivIcon').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"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/PixivIcon.ts","../src/charcoalIconFiles.ts","../src/loaders/PixivIconLoadError.ts","../src/loaders/CharcoalIconFilesLoader.ts","../src/loaders/CustomRawFileLoader.ts","../src/loaders/CustomIconLoader.ts","../src/loaders/index.ts","../src/ssr.ts","../src/index.ts"],"sourcesContent":["import type React from 'react'\nimport warning from 'warning'\nimport { KnownIconFile } from './charcoalIconFiles'\nimport { getIcon, addCustomIcon } from './loaders'\nimport { addRawFile } from './loaders/CustomRawFileLoader'\nimport { __SERVER__ } from './ssr'\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 /**\n * NOTE: icon content should be sanitized before pass to extend()\n *\n * XSSに注意すること。\n * 登録したファイルの中身が直接domに反映されるため、XSSに繋がる可能性があります。\n * 信用していないソースからアイコンを追加する場合dom-purifyなどを経由してください。\n */\n static extend(\n map: Extended extends true\n ? Record<ExtendedIconFile, string | (() => Promise<string>)>\n : Record<string, string | (() => Promise<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, filePathOrUrlOrImportFn]) => {\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 if (typeof filePathOrUrlOrImportFn === 'string') {\n addCustomIcon(name, filePathOrUrlOrImportFn)\n }\n\n if (typeof filePathOrUrlOrImportFn === 'function') {\n addRawFile(name, filePathOrUrlOrImportFn)\n }\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 if (!Number.isFinite(size)) {\n throw new TypeError(`icon size must not be NaN`)\n }\n\n const style = `<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\n const svg =\n this.svgContent !== undefined\n ? this.svgContent\n : `<svg viewBox=\"0 0 ${size} ${size}\"></svg>`\n\n // NOTE: User should sanitize the svg content before passing to charcoal.\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 this.svgContent = await getIcon(name)\n this.render()\n }\n\n private waitUntilVisible() {\n return new Promise<void>((resolve) => {\n this.observer = new IntersectionObserver(\n (entries) => {\n // In Chromium based browsers, multiple entries can be returned even only observe once.\n // Here, we don't care about the entry time but only if isIntersecting happened.\n const isIntersecting = entries.some((entry) => entry.isIntersecting)\n if (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","import charcoalIconFiles from '@charcoal-ui/icon-files'\n\nexport default charcoalIconFiles\nexport type KnownIconFile = keyof typeof charcoalIconFiles\nexport const KNOWN_ICON_FILES = Object.keys(\n charcoalIconFiles\n) as KnownIconFile[]\n\nexport function isKnownIconFile(name: string): name is KnownIconFile {\n return name in charcoalIconFiles\n}\n","export class PixivIconLoadError extends Error {\n constructor(name: string, cause: unknown) {\n const message = formatMessage(name, cause)\n\n super(message, { cause })\n this.name = 'PixivIconLoadError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nfunction formatMessage(name: string, cause: unknown) {\n const message = `Failed to fetch <pixiv-icon name=\"${name}\">`\n if (cause == null) {\n return message\n }\n\n if (cause instanceof Error) {\n return `${message}: ${cause.toString()})`\n }\n\n return `${message}: ${JSON.stringify(cause)})`\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\nimport charcoalIconFiles, { KnownIconFile } from '../charcoalIconFiles'\n\n/**\n * `@charcoal-ui/icon-files` に収録されているアイコンを取ってくる\n */\nexport class CharcoalIconFilesLoader implements Loadable {\n protected _name: KnownIconFile\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: KnownIconFile) {\n this._name = name\n }\n\n get importIconFile() {\n return charcoalIconFiles[this._name]\n }\n\n async fetch(): Promise<string> {\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.importIconFile()\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { KnownIconFile } from '../charcoalIconFiles'\nimport { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'\n\nexport class CustomRawFileLoader extends CharcoalIconFilesLoader {\n /**\n * icons-filesと同じ型のアイコンをしまっとくところ\n */\n static filePackages = new Map<string, () => Promise<string>>()\n\n get importIconFile() {\n const icon = CustomRawFileLoader.filePackages.get(this._name)\n if (icon !== undefined) return icon\n\n throw new Error('Custom icon file was not found')\n }\n}\n\nexport function addRawFile(name: string, importFn: () => Promise<string>) {\n CustomRawFileLoader.filePackages.set(name, importFn)\n}\n\n/**\n * 登録されているfile packagesにiconがあればtrue\n */\nexport function isKnownRawIconFile(name: string): name is KnownIconFile {\n return CustomRawFileLoader.filePackages.has(name)\n}\n","import { PixivIconLoadError } from './PixivIconLoadError'\nimport { Loadable } from './Loadable'\n\n/**\n * `PixivIcon.extend()` で登録されたカスタムのアイコンを取得する\n */\nexport class CustomIconLoader implements Loadable {\n private _name: string\n private _filePathOrUrl: string\n private _resultSvg: string | undefined = undefined\n private _promise: Promise<string> | undefined = undefined\n\n constructor(name: string, filePathOrUrl: string) {\n this._name = name\n this._filePathOrUrl = filePathOrUrl\n }\n\n async fetch(): Promise<string> {\n if (this._resultSvg !== undefined) {\n return this._resultSvg\n }\n\n if (this._promise) {\n return this._promise\n }\n\n this._promise = fetch(this._filePathOrUrl)\n .then((response) => {\n if (!response.ok) {\n throw new PixivIconLoadError(this._name, 'Response is not ok')\n }\n\n return response.text()\n })\n .then((svg) => {\n this._resultSvg = svg\n return this._resultSvg\n })\n .catch((e) => {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n throw new PixivIconLoadError(this._name, e)\n })\n .finally(() => {\n this._promise = undefined\n })\n\n return this._promise\n }\n}\n","import { isKnownIconFile } from '../charcoalIconFiles'\nimport { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'\nimport { CustomRawFileLoader, isKnownRawIconFile } from './CustomRawFileLoader'\nimport { CustomIconLoader } from './CustomIconLoader'\nimport { Loadable } from './Loadable'\nimport { PixivIconLoadError } from './PixivIconLoadError'\n\n/**\n * icon をロードするオブジェクトのプール。Loader のインスタンスは作り次第ここに入れる\n *\n * 同じアイコンへの複数回のリクエストが起きないためには、Loader がこの中でユニークでないと行けない\n */\nconst loaders = new Map<string, Loadable>()\n\nexport function addCustomIcon(name: string, filePathOrUrl: string) {\n loaders.set(name, new CustomIconLoader(name, filePathOrUrl))\n}\n\nexport async function getIcon(name: string) {\n const loader = resolveIconLoader(name)\n if (loader == null) {\n throw new PixivIconLoadError(name, 'Loader was not found')\n }\n\n try {\n const svg = await loader.fetch()\n if (typeof svg !== 'string') {\n // eslint-disable-next-line no-console\n console.warn(\n `${name}: Expected load result to be a string, but received an unexpected type.`\n )\n }\n\n return svg\n } catch (e) {\n if (e instanceof PixivIconLoadError) {\n throw e\n }\n throw new PixivIconLoadError(name, e)\n }\n}\n\nfunction resolveIconLoader(name: string) {\n // 登録済み or キャッシュ済みの場合\n // addCustomIcon で登録された CustomIconLoader は常にこっちを通る\n const registeredLoader = loaders.get(name)\n if (registeredLoader) {\n return registeredLoader\n }\n\n // addRawFile で登録されたもの\n if (isKnownRawIconFile(name)) {\n const customFilePackageLoader = new CustomRawFileLoader(name)\n loaders.set(name, customFilePackageLoader)\n return customFilePackageLoader\n }\n\n if (isKnownIconFile(name)) {\n // `@charcoal-ui/icon-files` に収録されているアイコンにはこれを返す\n const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name)\n loaders.set(name, charcoalIconFilesLoader)\n return charcoalIconFilesLoader\n }\n\n // 存在しないアイコンにはこれを返す\n return null\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-explicit-any\n globalThis.HTMLElement = class {} as any\n}\n","import { PixivIcon } from './PixivIcon'\nimport { __SERVER__ } from './ssr'\nexport { PixivIcon, type KnownIconType, type Props } from './PixivIcon'\nexport { KNOWN_ICON_FILES } from './charcoalIconFiles'\nexport { PixivIconLoadError } from './loaders/PixivIconLoadError'\n\ndeclare global {\n interface Window {\n PixivIcon: typeof PixivIcon\n }\n}\n\ndeclare module 'react' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'pixiv-icon': import('./PixivIcon').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"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,7 @@ import { KnownIconFile } from '../charcoalIconFiles';
|
|
|
4
4
|
* `@charcoal-ui/icon-files` に収録されているアイコンを取ってくる
|
|
5
5
|
*/
|
|
6
6
|
export declare class CharcoalIconFilesLoader implements Loadable {
|
|
7
|
-
|
|
7
|
+
protected _name: KnownIconFile;
|
|
8
8
|
private _resultSvg;
|
|
9
9
|
private _promise;
|
|
10
10
|
constructor(name: KnownIconFile);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CharcoalIconFilesLoader.d.ts","sourceRoot":"","sources":["../../src/loaders/CharcoalIconFilesLoader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAA0B,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEvE;;GAEG;AACH,qBAAa,uBAAwB,YAAW,QAAQ;IACtD,
|
|
1
|
+
{"version":3,"file":"CharcoalIconFilesLoader.d.ts","sourceRoot":"","sources":["../../src/loaders/CharcoalIconFilesLoader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAA0B,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEvE;;GAEG;AACH,qBAAa,uBAAwB,YAAW,QAAQ;IACtD,SAAS,CAAC,KAAK,EAAE,aAAa,CAAA;IAC9B,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,QAAQ,CAAyC;gBAE7C,IAAI,EAAE,aAAa;IAI/B,IAAI,cAAc,k6NAEjB;IAEK,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;CAuB/B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KnownIconFile } from '../charcoalIconFiles';
|
|
2
|
+
import { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader';
|
|
3
|
+
export declare class CustomRawFileLoader extends CharcoalIconFilesLoader {
|
|
4
|
+
/**
|
|
5
|
+
* icons-filesと同じ型のアイコンをしまっとくところ
|
|
6
|
+
*/
|
|
7
|
+
static filePackages: Map<string, () => Promise<string>>;
|
|
8
|
+
get importIconFile(): () => Promise<string>;
|
|
9
|
+
}
|
|
10
|
+
export declare function addRawFile(name: string, importFn: () => Promise<string>): void;
|
|
11
|
+
/**
|
|
12
|
+
* 登録されているfile packagesにiconがあればtrue
|
|
13
|
+
*/
|
|
14
|
+
export declare function isKnownRawIconFile(name: string): name is KnownIconFile;
|
|
15
|
+
//# sourceMappingURL=CustomRawFileLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomRawFileLoader.d.ts","sourceRoot":"","sources":["../../src/loaders/CustomRawFileLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAEnE,qBAAa,mBAAoB,SAAQ,uBAAuB;IAC9D;;OAEG;IACH,MAAM,CAAC,YAAY,oBAAyB,QAAQ,MAAM,CAAC,EAAG;IAE9D,IAAI,cAAc,0BAKjB;CACF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,QAEvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,aAAa,CAEtE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/loaders/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/loaders/index.ts"],"names":[],"mappings":"AAcA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,QAEhE;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,mBAsBzC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@charcoal-ui/icons",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/index.cjs.js",
|
|
6
6
|
"module": "./dist/index.esm.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"warning": "^4.0.3",
|
|
33
|
-
"@charcoal-ui/icon-files": "4.
|
|
33
|
+
"@charcoal-ui/icon-files": "4.6.0"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"src",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default '<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect width="16" height="16" x="0" y="0" fill="currentColor"></rect></svg>'
|
package/src/CustomIcon.mdx
CHANGED
|
@@ -4,6 +4,56 @@ import { Meta, Story, Canvas, Props } from '@storybook/addon-docs'
|
|
|
4
4
|
|
|
5
5
|
# 独自のアイコンを登録する
|
|
6
6
|
|
|
7
|
+
`<pixiv-icon>` に収録されていないアイコンを登録する方法が 2 つ用意されています。
|
|
8
|
+
|
|
9
|
+
## .js ファイルを登録する
|
|
10
|
+
|
|
11
|
+
`<pixiv-icon>` に収録されているアイコン同様の処理系を利用してアイコンをレンダリングします。
|
|
12
|
+
|
|
13
|
+
### 1. SVG タグ文字列を返す .js ファイルを作成する
|
|
14
|
+
|
|
15
|
+
表示したい SVG が文字列として返される `.js` のファイルを作成してください。
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
// ExampleCustomIcon.js
|
|
19
|
+
export default '<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect width="16" height="16" x="0" y="0" fill="currentColor"></rect></svg>'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`d.ts` も作成するならば以下のようになります。
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// ExampleCustomIcon.d.ts
|
|
26
|
+
declare const _default: string
|
|
27
|
+
export default _default
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. `PixivIcon.extend()` で登録する
|
|
31
|
+
|
|
32
|
+
作成した object を `PixivIcon.extend()` の引数へ与えて登録します。
|
|
33
|
+
名前の形式は `{size}/{name}` である必要があります。
|
|
34
|
+
TypeScript 環境では、`KnownIconType` という型を拡張することで、カスタムアイコンに対しても補完が効くようになります。
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { PixivIcon } from '@charcoal-ui/icons'
|
|
38
|
+
|
|
39
|
+
PixivIcon.extend({
|
|
40
|
+
'16/ExampleCustomIcon': () =>
|
|
41
|
+
import('./path/to/ExampleCustomIcon.js').then((m) => m.default),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
declare module '@charcoal-ui/icons' {
|
|
45
|
+
export interface KnownIconType {
|
|
46
|
+
'16/ExampleCustomIcon': unknown
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<pixiv-icon name="16/ExampleCustomIcon" />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## SVG ファイルを登録する
|
|
56
|
+
|
|
7
57
|
`<pixiv-icon>` の `name` に存在しない SVG をアイコンとして登録することができます。
|
|
8
58
|
|
|
9
59
|
```typescript
|
|
@@ -25,10 +75,10 @@ declare module '@charcoal-ui/icons' {
|
|
|
25
75
|
}
|
|
26
76
|
```
|
|
27
77
|
|
|
28
|
-
その場合も名前の形式は `{size}/{name}` である必要があります。
|
|
78
|
+
その場合も名前の形式は `{size}/{name}` である必要があります。
|
|
29
79
|
TypeScript 環境では、`KnownIconType` という型を拡張することで、カスタムアイコンに対しても補完が効くようになります。
|
|
30
80
|
|
|
31
|
-
|
|
81
|
+
### SVG ファイルアイコンのためのバンドラ設定
|
|
32
82
|
|
|
33
83
|
`PixivIcon.extend()` に渡すオブジェクトはキーがアイコン名、値が SVG ファイルに対するパス名または URL である必要があります(内部的には、ここで渡した文字列に対して `fetch()` が走ります)。
|
|
34
84
|
|
package/src/PixivIcon.story.tsx
CHANGED
|
@@ -2,13 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
import styled, { createGlobalStyle } from 'styled-components'
|
|
4
4
|
import TestIconThatNeverExists from './16/TestIconThatNeverExists.svg'
|
|
5
|
-
import { PixivIcon, Props } from '
|
|
5
|
+
import { PixivIcon, Props } from '@charcoal-ui/icons'
|
|
6
6
|
import { KnownIconFile, KNOWN_ICON_FILES } from './charcoalIconFiles'
|
|
7
7
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
8
8
|
|
|
9
|
+
declare module '.' {
|
|
10
|
+
export interface KnownIconType {
|
|
11
|
+
'16/TestIconThatNeverExists': string
|
|
12
|
+
'16/TestIconFileThatNeverExists': () => Promise<string>
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
PixivIcon.extend({
|
|
10
17
|
// かぶらなそうな名前をつける
|
|
11
18
|
'16/TestIconThatNeverExists': TestIconThatNeverExists,
|
|
19
|
+
'16/TestIconFileThatNeverExists': () =>
|
|
20
|
+
import('./16/TestIconThatNeverExists.js').then((m) => m.default),
|
|
12
21
|
})
|
|
13
22
|
|
|
14
23
|
const meta: Meta<Props> = {
|
|
@@ -168,3 +177,22 @@ export const WithUnsafe: StoryObj<Props> = {
|
|
|
168
177
|
color: '#000000',
|
|
169
178
|
},
|
|
170
179
|
}
|
|
180
|
+
|
|
181
|
+
export const RawIconFile: StoryObj<Props> = {
|
|
182
|
+
render: ({ color, name, scale }) => {
|
|
183
|
+
return (
|
|
184
|
+
<>
|
|
185
|
+
<IconDef color={color}>
|
|
186
|
+
<pixiv-icon name={name} scale={scale} />
|
|
187
|
+
アイコンと文字
|
|
188
|
+
</IconDef>
|
|
189
|
+
<Global />
|
|
190
|
+
</>
|
|
191
|
+
)
|
|
192
|
+
},
|
|
193
|
+
args: {
|
|
194
|
+
name: '16/TestIconFileThatNeverExists',
|
|
195
|
+
scale: 1,
|
|
196
|
+
color: '#000000',
|
|
197
|
+
},
|
|
198
|
+
}
|
package/src/PixivIcon.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type React from 'react'
|
|
|
2
2
|
import warning from 'warning'
|
|
3
3
|
import { KnownIconFile } from './charcoalIconFiles'
|
|
4
4
|
import { getIcon, addCustomIcon } from './loaders'
|
|
5
|
+
import { addRawFile } from './loaders/CustomRawFileLoader'
|
|
5
6
|
import { __SERVER__ } from './ssr'
|
|
6
7
|
|
|
7
8
|
const attributes = ['name', 'scale', 'unsafe-non-guideline-scale'] as const
|
|
@@ -34,25 +35,35 @@ export class PixivIcon extends HTMLElement {
|
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* NOTE: icon content should be sanitized before pass to extend()
|
|
38
|
+
*
|
|
39
|
+
* XSSに注意すること。
|
|
40
|
+
* 登録したファイルの中身が直接domに反映されるため、XSSに繋がる可能性があります。
|
|
41
|
+
* 信用していないソースからアイコンを追加する場合dom-purifyなどを経由してください。
|
|
37
42
|
*/
|
|
38
43
|
static extend(
|
|
39
44
|
map: Extended extends true
|
|
40
|
-
? Record<ExtendedIconFile, string>
|
|
41
|
-
: Record<string, string>
|
|
45
|
+
? Record<ExtendedIconFile, string | (() => Promise<string>)>
|
|
46
|
+
: Record<string, string | (() => Promise<string>)>
|
|
42
47
|
) {
|
|
43
48
|
warning(!__SERVER__, 'Using `PixivIcon.extend()` on server has no effect')
|
|
44
49
|
if (__SERVER__) {
|
|
45
50
|
return
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
Object.entries(map).forEach(([name,
|
|
53
|
+
Object.entries(map).forEach(([name, filePathOrUrlOrImportFn]) => {
|
|
49
54
|
if (!name.includes('/')) {
|
|
50
55
|
throw new TypeError(
|
|
51
56
|
`${name} is not a valid icon name. "name" must be named like [size]/[Name].`
|
|
52
57
|
)
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
if (typeof filePathOrUrlOrImportFn === 'string') {
|
|
61
|
+
addCustomIcon(name, filePathOrUrlOrImportFn)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof filePathOrUrlOrImportFn === 'function') {
|
|
65
|
+
addRawFile(name, filePathOrUrlOrImportFn)
|
|
66
|
+
}
|
|
56
67
|
})
|
|
57
68
|
}
|
|
58
69
|
|
|
@@ -6,7 +6,7 @@ import charcoalIconFiles, { KnownIconFile } from '../charcoalIconFiles'
|
|
|
6
6
|
* `@charcoal-ui/icon-files` に収録されているアイコンを取ってくる
|
|
7
7
|
*/
|
|
8
8
|
export class CharcoalIconFilesLoader implements Loadable {
|
|
9
|
-
|
|
9
|
+
protected _name: KnownIconFile
|
|
10
10
|
private _resultSvg: string | undefined = undefined
|
|
11
11
|
private _promise: Promise<string> | undefined = undefined
|
|
12
12
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { KnownIconFile } from '../charcoalIconFiles'
|
|
2
|
+
import { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'
|
|
3
|
+
|
|
4
|
+
export class CustomRawFileLoader extends CharcoalIconFilesLoader {
|
|
5
|
+
/**
|
|
6
|
+
* icons-filesと同じ型のアイコンをしまっとくところ
|
|
7
|
+
*/
|
|
8
|
+
static filePackages = new Map<string, () => Promise<string>>()
|
|
9
|
+
|
|
10
|
+
get importIconFile() {
|
|
11
|
+
const icon = CustomRawFileLoader.filePackages.get(this._name)
|
|
12
|
+
if (icon !== undefined) return icon
|
|
13
|
+
|
|
14
|
+
throw new Error('Custom icon file was not found')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function addRawFile(name: string, importFn: () => Promise<string>) {
|
|
19
|
+
CustomRawFileLoader.filePackages.set(name, importFn)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 登録されているfile packagesにiconがあればtrue
|
|
24
|
+
*/
|
|
25
|
+
export function isKnownRawIconFile(name: string): name is KnownIconFile {
|
|
26
|
+
return CustomRawFileLoader.filePackages.has(name)
|
|
27
|
+
}
|
package/src/loaders/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isKnownIconFile } from '../charcoalIconFiles'
|
|
2
2
|
import { CharcoalIconFilesLoader } from './CharcoalIconFilesLoader'
|
|
3
|
+
import { CustomRawFileLoader, isKnownRawIconFile } from './CustomRawFileLoader'
|
|
3
4
|
import { CustomIconLoader } from './CustomIconLoader'
|
|
4
5
|
import { Loadable } from './Loadable'
|
|
5
6
|
import { PixivIconLoadError } from './PixivIconLoadError'
|
|
@@ -21,13 +22,22 @@ export async function getIcon(name: string) {
|
|
|
21
22
|
throw new PixivIconLoadError(name, 'Loader was not found')
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
try {
|
|
26
|
+
const svg = await loader.fetch()
|
|
27
|
+
if (typeof svg !== 'string') {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.warn(
|
|
30
|
+
`${name}: Expected load result to be a string, but received an unexpected type.`
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return svg
|
|
35
|
+
} catch (e) {
|
|
25
36
|
if (e instanceof PixivIconLoadError) {
|
|
26
37
|
throw e
|
|
27
38
|
}
|
|
28
|
-
|
|
29
39
|
throw new PixivIconLoadError(name, e)
|
|
30
|
-
}
|
|
40
|
+
}
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
function resolveIconLoader(name: string) {
|
|
@@ -38,10 +48,16 @@ function resolveIconLoader(name: string) {
|
|
|
38
48
|
return registeredLoader
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
//
|
|
51
|
+
// addRawFile で登録されたもの
|
|
52
|
+
if (isKnownRawIconFile(name)) {
|
|
53
|
+
const customFilePackageLoader = new CustomRawFileLoader(name)
|
|
54
|
+
loaders.set(name, customFilePackageLoader)
|
|
55
|
+
return customFilePackageLoader
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
if (isKnownIconFile(name)) {
|
|
59
|
+
// `@charcoal-ui/icon-files` に収録されているアイコンにはこれを返す
|
|
43
60
|
const charcoalIconFilesLoader = new CharcoalIconFilesLoader(name)
|
|
44
|
-
|
|
45
61
|
loaders.set(name, charcoalIconFilesLoader)
|
|
46
62
|
return charcoalIconFilesLoader
|
|
47
63
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Props } from '.';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
declare const meta: Meta<Props>;
|
|
4
|
-
export default meta;
|
|
5
|
-
export declare const Default: StoryObj<Props>;
|
|
6
|
-
export declare const WithAttributes: StoryObj<Props>;
|
|
7
|
-
export declare const WithUnsafe: StoryObj<Props>;
|
|
8
|
-
//# sourceMappingURL=PixivIcon.story.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PixivIcon.story.d.ts","sourceRoot":"","sources":["../src/PixivIcon.story.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,EAAE,MAAM,GAAG,CAAA;AAEpC,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAOtD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,KAAK,CA8CrB,CAAA;AAED,eAAe,IAAI,CAAA;AAkEnB,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,KAAK,CAEnC,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,KAAK,CAe1C,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,KAAK,CAqBtC,CAAA"}
|