@raqm/ui 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ releases may include breaking changes.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-11-14
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Keep `RaqmIcon` artwork inside its `1em` host: cap the SVG at the host box and
|
|
15
|
+
center it, so surrounding SVG rules (such as the Button residual) can no longer push
|
|
16
|
+
the glyph off-center.
|
|
17
|
+
|
|
10
18
|
## [0.2.1] - 2026-11-13
|
|
11
19
|
|
|
12
20
|
### Changed
|
|
@@ -95,7 +95,7 @@ var RaqmIcon = class RaqmIcon {
|
|
|
95
95
|
}
|
|
96
96
|
`,
|
|
97
97
|
isInline: true,
|
|
98
|
-
styles: [":where(:host){display:inline-flex;flex:none;inline-size:1em;block-size:1em;vertical-align:-.125em}:where(svg){display:block;inline-size:100%;block-size:100%;fill:currentColor}\n"],
|
|
98
|
+
styles: [":where(:host){display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:1em;block-size:1em;vertical-align:-.125em}:where(svg){display:block;inline-size:100%;block-size:100%;max-inline-size:100%;max-block-size:100%;fill:currentColor}\n"],
|
|
99
99
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
100
100
|
});
|
|
101
101
|
};
|
|
@@ -132,7 +132,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
132
132
|
class: "raqm-icon",
|
|
133
133
|
"[attr.data-raqm-icon]": "name()"
|
|
134
134
|
},
|
|
135
|
-
styles: [":where(:host){display:inline-flex;flex:none;inline-size:1em;block-size:1em;vertical-align:-.125em}:where(svg){display:block;inline-size:100%;block-size:100%;fill:currentColor}\n"]
|
|
135
|
+
styles: [":where(:host){display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:1em;block-size:1em;vertical-align:-.125em}:where(svg){display:block;inline-size:100%;block-size:100%;max-inline-size:100%;max-block-size:100%;fill:currentColor}\n"]
|
|
136
136
|
}]
|
|
137
137
|
}],
|
|
138
138
|
ctorParameters: () => [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raqm-ui-icon.mjs","names":[],"sources":["../../../projects/ui/icon/icon.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n InjectionToken,\n Provider,\n computed,\n effect,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\n\n/**\n * One icon artwork, expressed as SVG path data. The package ships no icons; consumers\n * register their own map with {@link provideRaqmIcons}.\n */\nexport interface RaqmIconDefinition {\n /** The SVG `viewBox`, for example `0 0 24 24`. */\n readonly viewBox: string;\n /** One or more path `d` values; every path is filled with `currentColor`. */\n readonly paths: readonly string[];\n /** Fill rule for multi-path marks that rely on even-odd holes. */\n readonly fillRule?: 'nonzero' | 'evenodd';\n /** Clip rule for multi-path marks that rely on even-odd holes. */\n readonly clipRule?: 'nonzero' | 'evenodd';\n /** Fallback accessible name used when the consumer does not pass `label`. */\n readonly title?: string;\n}\n\n/** Consumer icon registry, keyed by the `name` used on `<raqm-icon>`. */\nexport type RaqmIconMap = Readonly<Record<string, RaqmIconDefinition>>;\n\n// One warning per problem, not one per rendered row.\nconst warned = new Set<string>();\n\nfunction warnOnce(key: string, message: string): void {\n if (warned.has(key)) return;\n warned.add(key);\n console.warn(message);\n}\n\nconst hasIcon = (icons: RaqmIconMap, name: string): boolean => Object.hasOwn(icons, name);\n\n/** Icon registry token. Empty by default; fill it with {@link provideRaqmIcons}. */\nexport const RAQM_ICONS = new InjectionToken<RaqmIconMap>('RAQM_ICONS', {\n factory: () => ({}),\n});\n\n/**\n * Registers consumer icon data for `RaqmIcon`.\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [provideRaqmIcons({ home: HOME_ICON })],\n * });\n * ```\n *\n * Call it once at the application or route level; a later call replaces the whole map.\n */\nexport function provideRaqmIcons(icons: RaqmIconMap): Provider {\n return { provide: RAQM_ICONS, useValue: icons };\n}\n\n/**\n * Inline SVG icon rendered from consumer-supplied path data.\n *\n * Decorative by default (`aria-hidden`); pass `label` (or set `title` on the definition)\n * to expose it as `role=\"img\"` with an accessible name. Unknown names render nothing and\n * warn in development.\n */\n@Component({\n selector: 'raqm-icon',\n template: `\n @if (definition(); as icon) {\n <svg\n [attr.viewBox]=\"icon.viewBox\"\n [attr.role]=\"accessibleName() ? 'img' : null\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-hidden]=\"accessibleName() ? null : 'true'\"\n focusable=\"false\"\n >\n @for (path of icon.paths; track $index) {\n <path\n [attr.d]=\"path\"\n [attr.fill-rule]=\"icon.fillRule ?? null\"\n [attr.clip-rule]=\"icon.clipRule ?? null\"\n />\n }\n </svg>\n }\n `,\n styleUrl: './icon.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'raqm-icon',\n '[attr.data-raqm-icon]': 'name()',\n },\n})\nexport class RaqmIcon {\n /** Registry key of the artwork to render. */\n readonly name = input.required<string>();\n\n /** Accessible name. Omit it for decorative icons. */\n readonly label = input<string | null>(null);\n\n private readonly icons = inject(RAQM_ICONS);\n\n protected readonly definition = computed<RaqmIconDefinition | null>(() => {\n const name = this.name();\n if (!hasIcon(this.icons, name)) return null;\n const definition = this.icons[name];\n // A definition without paths would render an invisible, empty <svg>.\n return definition?.paths?.length ? definition : null;\n });\n\n protected readonly accessibleName = computed(() => {\n const label = this.label();\n // An explicit label, even an empty one, decides the name: `label=\"\"` marks a\n // titled definition as decorative in this placement.\n if (label !== null && label !== undefined) return label.trim() || null;\n return this.definition()?.title?.trim() || null;\n });\n\n constructor() {\n if (!isDevMode()) return;\n\n // Names arrive from templates, so a typo would otherwise fail silently.\n effect(() => {\n const name = this.name();\n if (!hasIcon(this.icons, name)) {\n warnOnce(\n `unknown:${name}`,\n `[RaqmIcon] Unknown icon \"${name}\". Register it with provideRaqmIcons().`,\n );\n return;\n }\n if (!this.icons[name]?.paths?.length) {\n warnOnce(\n `empty:${name}`,\n `[RaqmIcon] Icon \"${name}\" has no paths. Add at least one path to its definition.`,\n );\n }\n });\n }\n}\n"],"mappings":";;AAiCA,MAAM,yBAAS,IAAI,IAAG;AAEtB,SAAS,SAAS,KAAa,SAAe;CAC5C,IAAI,OAAO,IAAI,GAAG,GAAG;CACrB,OAAO,IAAI,GAAG;CACd,QAAQ,KAAK,OAAO;AACtB;AAEA,MAAM,WAAW,OAAoB,SAA0B,OAAO,OAAO,OAAO,IAAI;AAGxF,MAAa,aAAa,IAAI,eAA4B,cAAc,EACtE,gBAAgB,CAAA,GACjB,CAAA;AAaD,SAAgB,iBAAiB,OAAkB;CACjD,OAAO;EAAE,SAAS;EAAY,UAAU;CAAK;AAC/C;AAqCA,IAAa,WAAb,MAAa,SAAQ;CAEV,OAAO,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG/B,QAAQ,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAE1B,QAAQ,OAAO,UAAU;CAEvB,aAAa,eAAyC;EACvE,MAAM,OAAO,KAAK,KAAI;EACtB,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,OAAO;EACvC,MAAM,aAAa,KAAK,MAAM;EAE9B,OAAO,YAAY,OAAO,SAAS,aAAa;CAClD,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,iBAAiB,eAAc;EAChD,MAAM,QAAQ,KAAK,MAAK;EAGxB,IAAI,UAAU,QAAQ,UAAU,QAAW,OAAO,MAAM,KAAI,KAAM;EAClE,OAAO,KAAK,WAAU,CAAE,EAAE,OAAO,KAAI,KAAM;CAC7C,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,cAAA;EACE,IAAI,CAAC,UAAS,GAAI;EAGlB,aAAY;GACV,MAAM,OAAO,KAAK,KAAI;GACtB,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG;IAC9B,SACE,WAAW,QACX,4BAA4B,KAAI,wCAAyC;IAE3E;GACF;GACA,IAAI,CAAC,KAAK,MAAM,KAAK,EAAE,OAAO,QAC5B,SACE,SAAS,QACT,oBAAoB,KAAI,yDAA0D;EAGxF,CAAC;CACH;CA7CW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAQ,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAR,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAQ,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA;GAAA,YAAA,EAAA,uBAAA,SAAA;GAAA,gBAAA;EAAA;EAAA,UAAA;EAAA,UA1BT;;;;;;;;;;;;;;;;;;;EAkBT,UAAA;EAAA,QAAA,CAAA,
|
|
1
|
+
{"version":3,"file":"raqm-ui-icon.mjs","names":[],"sources":["../../../projects/ui/icon/icon.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n InjectionToken,\n Provider,\n computed,\n effect,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\n\n/**\n * One icon artwork, expressed as SVG path data. The package ships no icons; consumers\n * register their own map with {@link provideRaqmIcons}.\n */\nexport interface RaqmIconDefinition {\n /** The SVG `viewBox`, for example `0 0 24 24`. */\n readonly viewBox: string;\n /** One or more path `d` values; every path is filled with `currentColor`. */\n readonly paths: readonly string[];\n /** Fill rule for multi-path marks that rely on even-odd holes. */\n readonly fillRule?: 'nonzero' | 'evenodd';\n /** Clip rule for multi-path marks that rely on even-odd holes. */\n readonly clipRule?: 'nonzero' | 'evenodd';\n /** Fallback accessible name used when the consumer does not pass `label`. */\n readonly title?: string;\n}\n\n/** Consumer icon registry, keyed by the `name` used on `<raqm-icon>`. */\nexport type RaqmIconMap = Readonly<Record<string, RaqmIconDefinition>>;\n\n// One warning per problem, not one per rendered row.\nconst warned = new Set<string>();\n\nfunction warnOnce(key: string, message: string): void {\n if (warned.has(key)) return;\n warned.add(key);\n console.warn(message);\n}\n\nconst hasIcon = (icons: RaqmIconMap, name: string): boolean => Object.hasOwn(icons, name);\n\n/** Icon registry token. Empty by default; fill it with {@link provideRaqmIcons}. */\nexport const RAQM_ICONS = new InjectionToken<RaqmIconMap>('RAQM_ICONS', {\n factory: () => ({}),\n});\n\n/**\n * Registers consumer icon data for `RaqmIcon`.\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [provideRaqmIcons({ home: HOME_ICON })],\n * });\n * ```\n *\n * Call it once at the application or route level; a later call replaces the whole map.\n */\nexport function provideRaqmIcons(icons: RaqmIconMap): Provider {\n return { provide: RAQM_ICONS, useValue: icons };\n}\n\n/**\n * Inline SVG icon rendered from consumer-supplied path data.\n *\n * Decorative by default (`aria-hidden`); pass `label` (or set `title` on the definition)\n * to expose it as `role=\"img\"` with an accessible name. Unknown names render nothing and\n * warn in development.\n */\n@Component({\n selector: 'raqm-icon',\n template: `\n @if (definition(); as icon) {\n <svg\n [attr.viewBox]=\"icon.viewBox\"\n [attr.role]=\"accessibleName() ? 'img' : null\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-hidden]=\"accessibleName() ? null : 'true'\"\n focusable=\"false\"\n >\n @for (path of icon.paths; track $index) {\n <path\n [attr.d]=\"path\"\n [attr.fill-rule]=\"icon.fillRule ?? null\"\n [attr.clip-rule]=\"icon.clipRule ?? null\"\n />\n }\n </svg>\n }\n `,\n styleUrl: './icon.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'raqm-icon',\n '[attr.data-raqm-icon]': 'name()',\n },\n})\nexport class RaqmIcon {\n /** Registry key of the artwork to render. */\n readonly name = input.required<string>();\n\n /** Accessible name. Omit it for decorative icons. */\n readonly label = input<string | null>(null);\n\n private readonly icons = inject(RAQM_ICONS);\n\n protected readonly definition = computed<RaqmIconDefinition | null>(() => {\n const name = this.name();\n if (!hasIcon(this.icons, name)) return null;\n const definition = this.icons[name];\n // A definition without paths would render an invisible, empty <svg>.\n return definition?.paths?.length ? definition : null;\n });\n\n protected readonly accessibleName = computed(() => {\n const label = this.label();\n // An explicit label, even an empty one, decides the name: `label=\"\"` marks a\n // titled definition as decorative in this placement.\n if (label !== null && label !== undefined) return label.trim() || null;\n return this.definition()?.title?.trim() || null;\n });\n\n constructor() {\n if (!isDevMode()) return;\n\n // Names arrive from templates, so a typo would otherwise fail silently.\n effect(() => {\n const name = this.name();\n if (!hasIcon(this.icons, name)) {\n warnOnce(\n `unknown:${name}`,\n `[RaqmIcon] Unknown icon \"${name}\". Register it with provideRaqmIcons().`,\n );\n return;\n }\n if (!this.icons[name]?.paths?.length) {\n warnOnce(\n `empty:${name}`,\n `[RaqmIcon] Icon \"${name}\" has no paths. Add at least one path to its definition.`,\n );\n }\n });\n }\n}\n"],"mappings":";;AAiCA,MAAM,yBAAS,IAAI,IAAG;AAEtB,SAAS,SAAS,KAAa,SAAe;CAC5C,IAAI,OAAO,IAAI,GAAG,GAAG;CACrB,OAAO,IAAI,GAAG;CACd,QAAQ,KAAK,OAAO;AACtB;AAEA,MAAM,WAAW,OAAoB,SAA0B,OAAO,OAAO,OAAO,IAAI;AAGxF,MAAa,aAAa,IAAI,eAA4B,cAAc,EACtE,gBAAgB,CAAA,GACjB,CAAA;AAaD,SAAgB,iBAAiB,OAAkB;CACjD,OAAO;EAAE,SAAS;EAAY,UAAU;CAAK;AAC/C;AAqCA,IAAa,WAAb,MAAa,SAAQ;CAEV,OAAO,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG/B,QAAQ,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAE1B,QAAQ,OAAO,UAAU;CAEvB,aAAa,eAAyC;EACvE,MAAM,OAAO,KAAK,KAAI;EACtB,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,OAAO;EACvC,MAAM,aAAa,KAAK,MAAM;EAE9B,OAAO,YAAY,OAAO,SAAS,aAAa;CAClD,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,iBAAiB,eAAc;EAChD,MAAM,QAAQ,KAAK,MAAK;EAGxB,IAAI,UAAU,QAAQ,UAAU,QAAW,OAAO,MAAM,KAAI,KAAM;EAClE,OAAO,KAAK,WAAU,CAAE,EAAE,OAAO,KAAI,KAAM;CAC7C,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,cAAA;EACE,IAAI,CAAC,UAAS,GAAI;EAGlB,aAAY;GACV,MAAM,OAAO,KAAK,KAAI;GACtB,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG;IAC9B,SACE,WAAW,QACX,4BAA4B,KAAI,wCAAyC;IAE3E;GACF;GACA,IAAI,CAAC,KAAK,MAAM,KAAK,EAAE,OAAO,QAC5B,SACE,SAAS,QACT,oBAAoB,KAAI,yDAA0D;EAGxF,CAAC;CACH;CA7CW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAQ,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAR,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAQ,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA;GAAA,YAAA,EAAA,uBAAA,SAAA;GAAA,gBAAA;EAAA;EAAA,UAAA;EAAA,UA1BT;;;;;;;;;;;;;;;;;;;EAkBT,UAAA;EAAA,QAAA,CAAA,sQAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAQU,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAQ,YAAA,CAAA;EA5BpB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAW,UACX;;;;;;;;;;;;;;;;;;;GAkBT,iBAEgB,wBAAwB;GAAM,MACzC;IACJ,OAAO;IACP,yBAAyB;GAC1B;GAAA,QAAA,CAAA,sQAAA;EAAA,CAAA"}
|
package/package.json
CHANGED