@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.
@@ -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?;
@@ -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;AAQnD,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;;OAEG;IACH,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,QAAQ,SAAS,IAAI,GACtB,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAkB5B,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"}
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
- return loader.fetch().catch((e) => {
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, filePathOrUrl]) => {
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
- addCustomIcon(name, filePathOrUrl);
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() {
@@ -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
- return loader.fetch().catch((e) => {
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, filePathOrUrl]) => {
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
- addCustomIcon(name, filePathOrUrl);
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() {
@@ -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
- private _name;
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,OAAO,CAAC,KAAK,CAAe;IAC5B,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"}
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":"AAaA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,QAEhE;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,mBAazC"}
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.5.0",
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.5.0"
33
+ "@charcoal-ui/icon-files": "4.6.0"
34
34
  },
35
35
  "files": [
36
36
  "src",
@@ -0,0 +1,2 @@
1
+ declare const _default: string
2
+ export default _default
@@ -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>'
@@ -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
 
@@ -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, filePathOrUrl]) => {
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
- addCustomIcon(name, filePathOrUrl)
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
- private _name: KnownIconFile
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
+ }
@@ -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
- return loader.fetch().catch((e) => {
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
- // `@charcoal-ui/icon-files` に収録されているアイコンにはこれを返す
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"}