@khanacademy/wonder-blocks-icon 5.3.22 → 6.0.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-icon
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- a1f5a09: `PhosphorIcon` now automatically mirrors directional icons in RTL via a central allowlist, so call sites must stop swapping icons on `isRtl` (or pass `mirrorInRtl` to override per call site) to avoid double-flipping.
|
|
8
|
+
|
|
9
|
+
## 5.3.23
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [357a19c]
|
|
14
|
+
- Updated dependencies [6e4158c]
|
|
15
|
+
- @khanacademy/wonder-blocks-tokens@17.3.0
|
|
16
|
+
|
|
3
17
|
## 5.3.22
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -28,6 +28,13 @@ import { IconSize, PhosphorIconAsset } from "../types";
|
|
|
28
28
|
* - medium: 2.4rem (24px)
|
|
29
29
|
* - large: 4.8rem (48px)
|
|
30
30
|
* - xlarge: 9.6rem (96px)
|
|
31
|
+
*
|
|
32
|
+
* ## Right-to-left
|
|
33
|
+
*
|
|
34
|
+
* Directional glyphs (arrows, carets, and similar) are mirrored automatically
|
|
35
|
+
* in RTL via a central allowlist. Pass the LTR-facing icon — do not swap icons
|
|
36
|
+
* based on `isRtl`. Use `mirrorInRtl` to override the allowlist per call site.
|
|
37
|
+
* See the PhosphorIcon RTL Mirroring docs for the list.
|
|
31
38
|
*/
|
|
32
39
|
export declare const PhosphorIcon: React.ForwardRefExoticComponent<Pick<AriaProps, "aria-hidden" | "aria-label" | "role"> & {
|
|
33
40
|
/**
|
|
@@ -70,4 +77,14 @@ export declare const PhosphorIcon: React.ForwardRefExoticComponent<Pick<AriaProp
|
|
|
70
77
|
* - `string`: an import referencing an arbitrary SVG file.
|
|
71
78
|
*/
|
|
72
79
|
icon: PhosphorIconAsset | string;
|
|
80
|
+
/**
|
|
81
|
+
* Whether to mirror this icon's glyph in right-to-left content, overriding
|
|
82
|
+
* the directional allowlist.
|
|
83
|
+
*
|
|
84
|
+
* - `true`: mirror even if the icon is not allowlisted (e.g. a custom
|
|
85
|
+
* directional SVG).
|
|
86
|
+
* - `false`: do not mirror even if the icon is allowlisted.
|
|
87
|
+
* - omitted: use the allowlist.
|
|
88
|
+
*/
|
|
89
|
+
mirrorInRtl?: boolean;
|
|
73
90
|
} & React.RefAttributes<HTMLSpanElement>>;
|
package/dist/es/index.js
CHANGED
|
@@ -6,7 +6,11 @@ import { sizing } from '@khanacademy/wonder-blocks-tokens';
|
|
|
6
6
|
|
|
7
7
|
function getSize$1(size){switch(size){case"small":default:{return styles$1.small}case"medium":{return styles$1.medium}case"large":{return styles$1.large}case"xlarge":{return styles$1.xlarge}}}const StyledDiv=addStyle("div");const Icon=React.forwardRef((props,ref)=>{const{size="small",id,testId,style,children,...otherProps}=props;const childrenElement=React.cloneElement(children,{style:{width:"100%",height:"100%"}});return jsx(StyledDiv,{style:[getSize$1(size),style],id:id,"data-testid":testId,ref:ref,...otherProps,children:childrenElement})});const styles$1=StyleSheet.create({small:{width:sizing.size_160,height:sizing.size_160},medium:{width:sizing.size_240,height:sizing.size_240},large:{width:sizing.size_480,height:sizing.size_480},xlarge:{width:sizing.size_960,height:sizing.size_960}});
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const MIRRORED_ICON_NAMES=new Set(["arrow-arc-left","arrow-arc-right","arrow-bend-double-up-left","arrow-bend-double-up-right","arrow-bend-down-left","arrow-bend-down-right","arrow-bend-left-down","arrow-bend-left-up","arrow-bend-right-down","arrow-bend-right-up","arrow-bend-up-left","arrow-bend-up-right","arrow-circle-down-left","arrow-circle-down-right","arrow-circle-left","arrow-circle-right","arrow-circle-up-left","arrow-circle-up-right","arrow-down-left","arrow-down-right","arrow-elbow-down-left","arrow-elbow-down-right","arrow-elbow-left","arrow-elbow-left-down","arrow-elbow-left-up","arrow-elbow-right","arrow-elbow-right-down","arrow-elbow-right-up","arrow-elbow-up-left","arrow-elbow-up-right","arrow-fat-left","arrow-fat-line-left","arrow-fat-line-right","arrow-fat-lines-left","arrow-fat-lines-right","arrow-fat-right","arrow-left","arrow-line-down-left","arrow-line-down-right","arrow-line-left","arrow-line-right","arrow-line-up-left","arrow-line-up-right","arrow-right","arrow-square-down-left","arrow-square-down-right","arrow-square-left","arrow-square-right","arrow-u-down-left","arrow-u-down-right","arrow-u-left-down","arrow-u-left-up","arrow-u-right-down","arrow-u-right-up","arrow-u-up-left","arrow-u-up-right","arrow-up-left","caret-circle-double-left","caret-circle-double-right","caret-circle-left","caret-circle-right","caret-double-left","caret-double-right","caret-left","caret-line-left","caret-line-right","caret-right","paper-plane","paper-plane-right","paper-plane-tilt","sign-in","sign-out","text-indent","text-outdent"]);
|
|
10
|
+
|
|
11
|
+
const WEIGHT_SUFFIXES=["-bold","-fill","-thin","-light","-duotone"];function getPhosphorIconName(icon){if(!icon||icon.startsWith("data:")){return undefined}const path=icon.split(/[?#]/)[0];const basename=path.substring(path.lastIndexOf("/")+1);if(!basename.toLowerCase().endsWith(".svg")){return undefined}const withoutExtension=basename.slice(0,-".svg".length);for(const suffix of WEIGHT_SUFFIXES){if(withoutExtension.endsWith(suffix)){return withoutExtension.slice(0,-suffix.length)}}return withoutExtension}function shouldMirrorIconInRtl(icon){const name=getPhosphorIconName(icon);if(!name){return false}const segments=name.split("-");for(let i=0;i<segments.length;i++){if(MIRRORED_ICON_NAMES.has(segments.slice(i).join("-"))){return true}}for(let end=1;end<segments.length;end++){if(MIRRORED_ICON_NAMES.has(segments.slice(0,end).join("-"))){return true}}return false}function resolveMirrorInRtl(icon,mirrorInRtl){return mirrorInRtl??shouldMirrorIconInRtl(icon)}
|
|
12
|
+
|
|
13
|
+
const StyledSpan=addStyle("span");const PhosphorIcon=React.forwardRef(function PhosphorIcon(props,ref){const{color="currentColor",icon,size="small",style,testId,className,role,mirrorInRtl,...sharedProps}=props;const sizeStyles=getSize(size);const classNames=`${className??""}`;const shouldMirror=resolveMirrorInRtl(icon,mirrorInRtl);return jsx(StyledSpan,{...sharedProps,className:classNames,style:[styles.svg,sizeStyles,{backgroundColor:color,maskImage:`url(${icon})`},shouldMirror&&styles.mirroredInRtl,style],"data-testid":testId,ref:ref,role:role??sharedProps["aria-label"]?"img":undefined})});function getSize(size){switch(size){case"small":default:return styles.small;case"medium":return styles.medium;case"large":return styles.large;case"xlarge":return styles.xlarge}}const styles=StyleSheet.create({svg:{display:"inline-block",verticalAlign:"text-bottom",flexShrink:0,flexGrow:0,maskSize:"100%",maskRepeat:"no-repeat",maskPosition:"center"},mirroredInRtl:{[":dir(rtl)"]:{transform:"scaleX(-1)"}},small:{width:sizing.size_160,height:sizing.size_160},medium:{width:sizing.size_240,height:sizing.size_240},large:{width:sizing.size_480,height:sizing.size_480},xlarge:{width:sizing.size_960,height:sizing.size_960}});PhosphorIcon.displayName="PhosphorIcon";
|
|
10
14
|
|
|
11
15
|
function useImageRoleAttributes(props){const{"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy}=props;const presentationOnlyAttributes={"aria-hidden":true};const iconMeaningAttributes={"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,role:"img"};const attributes=ariaLabel||ariaLabelledBy?iconMeaningAttributes:presentationOnlyAttributes;return attributes}
|
|
12
16
|
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,11 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
|
30
30
|
|
|
31
31
|
function getSize$1(size){switch(size){case"small":default:{return styles$1.small}case"medium":{return styles$1.medium}case"large":{return styles$1.large}case"xlarge":{return styles$1.xlarge}}}const StyledDiv=wonderBlocksCore.addStyle("div");const Icon=React__namespace.forwardRef((props,ref)=>{const{size="small",id,testId,style,children,...otherProps}=props;const childrenElement=React__namespace.cloneElement(children,{style:{width:"100%",height:"100%"}});return jsxRuntime.jsx(StyledDiv,{style:[getSize$1(size),style],id:id,"data-testid":testId,ref:ref,...otherProps,children:childrenElement})});const styles$1=aphrodite.StyleSheet.create({small:{width:wonderBlocksTokens.sizing.size_160,height:wonderBlocksTokens.sizing.size_160},medium:{width:wonderBlocksTokens.sizing.size_240,height:wonderBlocksTokens.sizing.size_240},large:{width:wonderBlocksTokens.sizing.size_480,height:wonderBlocksTokens.sizing.size_480},xlarge:{width:wonderBlocksTokens.sizing.size_960,height:wonderBlocksTokens.sizing.size_960}});
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const MIRRORED_ICON_NAMES=new Set(["arrow-arc-left","arrow-arc-right","arrow-bend-double-up-left","arrow-bend-double-up-right","arrow-bend-down-left","arrow-bend-down-right","arrow-bend-left-down","arrow-bend-left-up","arrow-bend-right-down","arrow-bend-right-up","arrow-bend-up-left","arrow-bend-up-right","arrow-circle-down-left","arrow-circle-down-right","arrow-circle-left","arrow-circle-right","arrow-circle-up-left","arrow-circle-up-right","arrow-down-left","arrow-down-right","arrow-elbow-down-left","arrow-elbow-down-right","arrow-elbow-left","arrow-elbow-left-down","arrow-elbow-left-up","arrow-elbow-right","arrow-elbow-right-down","arrow-elbow-right-up","arrow-elbow-up-left","arrow-elbow-up-right","arrow-fat-left","arrow-fat-line-left","arrow-fat-line-right","arrow-fat-lines-left","arrow-fat-lines-right","arrow-fat-right","arrow-left","arrow-line-down-left","arrow-line-down-right","arrow-line-left","arrow-line-right","arrow-line-up-left","arrow-line-up-right","arrow-right","arrow-square-down-left","arrow-square-down-right","arrow-square-left","arrow-square-right","arrow-u-down-left","arrow-u-down-right","arrow-u-left-down","arrow-u-left-up","arrow-u-right-down","arrow-u-right-up","arrow-u-up-left","arrow-u-up-right","arrow-up-left","caret-circle-double-left","caret-circle-double-right","caret-circle-left","caret-circle-right","caret-double-left","caret-double-right","caret-left","caret-line-left","caret-line-right","caret-right","paper-plane","paper-plane-right","paper-plane-tilt","sign-in","sign-out","text-indent","text-outdent"]);
|
|
34
|
+
|
|
35
|
+
const WEIGHT_SUFFIXES=["-bold","-fill","-thin","-light","-duotone"];function getPhosphorIconName(icon){if(!icon||icon.startsWith("data:")){return undefined}const path=icon.split(/[?#]/)[0];const basename=path.substring(path.lastIndexOf("/")+1);if(!basename.toLowerCase().endsWith(".svg")){return undefined}const withoutExtension=basename.slice(0,-".svg".length);for(const suffix of WEIGHT_SUFFIXES){if(withoutExtension.endsWith(suffix)){return withoutExtension.slice(0,-suffix.length)}}return withoutExtension}function shouldMirrorIconInRtl(icon){const name=getPhosphorIconName(icon);if(!name){return false}const segments=name.split("-");for(let i=0;i<segments.length;i++){if(MIRRORED_ICON_NAMES.has(segments.slice(i).join("-"))){return true}}for(let end=1;end<segments.length;end++){if(MIRRORED_ICON_NAMES.has(segments.slice(0,end).join("-"))){return true}}return false}function resolveMirrorInRtl(icon,mirrorInRtl){return mirrorInRtl??shouldMirrorIconInRtl(icon)}
|
|
36
|
+
|
|
37
|
+
const StyledSpan=wonderBlocksCore.addStyle("span");const PhosphorIcon=React__namespace.forwardRef(function PhosphorIcon(props,ref){const{color="currentColor",icon,size="small",style,testId,className,role,mirrorInRtl,...sharedProps}=props;const sizeStyles=getSize(size);const classNames=`${className??""}`;const shouldMirror=resolveMirrorInRtl(icon,mirrorInRtl);return jsxRuntime.jsx(StyledSpan,{...sharedProps,className:classNames,style:[styles.svg,sizeStyles,{backgroundColor:color,maskImage:`url(${icon})`},shouldMirror&&styles.mirroredInRtl,style],"data-testid":testId,ref:ref,role:role??sharedProps["aria-label"]?"img":undefined})});function getSize(size){switch(size){case"small":default:return styles.small;case"medium":return styles.medium;case"large":return styles.large;case"xlarge":return styles.xlarge}}const styles=aphrodite.StyleSheet.create({svg:{display:"inline-block",verticalAlign:"text-bottom",flexShrink:0,flexGrow:0,maskSize:"100%",maskRepeat:"no-repeat",maskPosition:"center"},mirroredInRtl:{[":dir(rtl)"]:{transform:"scaleX(-1)"}},small:{width:wonderBlocksTokens.sizing.size_160,height:wonderBlocksTokens.sizing.size_160},medium:{width:wonderBlocksTokens.sizing.size_240,height:wonderBlocksTokens.sizing.size_240},large:{width:wonderBlocksTokens.sizing.size_480,height:wonderBlocksTokens.sizing.size_480},xlarge:{width:wonderBlocksTokens.sizing.size_960,height:wonderBlocksTokens.sizing.size_960}});PhosphorIcon.displayName="PhosphorIcon";
|
|
34
38
|
|
|
35
39
|
function useImageRoleAttributes(props){const{"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy}=props;const presentationOnlyAttributes={"aria-hidden":true};const iconMeaningAttributes={"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,role:"img"};const attributes=ariaLabel||ariaLabelledBy?iconMeaningAttributes:presentationOnlyAttributes;return attributes}
|
|
36
40
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the Phosphor icon name from a resolved asset reference.
|
|
3
|
+
*
|
|
4
|
+
* The `icon` prop is whatever the bundler produced for the SVG import, so the
|
|
5
|
+
* shape varies by environment:
|
|
6
|
+
* - Vite dev server: `/node_modules/.../assets/regular/arrow-right.svg`
|
|
7
|
+
* - Vite production / Storybook build: `/assets/arrow-right-[hash].svg`
|
|
8
|
+
* - rspack `asset/resource`: `/images/[hash]-arrow-right.svg`
|
|
9
|
+
* - Jest transform: `arrow-right.svg`
|
|
10
|
+
*
|
|
11
|
+
* All of these end in `<name>[-weight].svg`, optionally behind a content hash
|
|
12
|
+
* and a directory prefix, so the name is recovered from the final path
|
|
13
|
+
* segment. Returns `undefined` when no name can be recovered — most notably
|
|
14
|
+
* for `data:` URIs, which carry no file name at all.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getPhosphorIconName(icon: string): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Whether the given icon asset should be mirrored in right-to-left content.
|
|
19
|
+
*
|
|
20
|
+
* Bundlers insert a content hash next to the icon name:
|
|
21
|
+
* - rspack `asset/resource`: `[hash]-[name][ext]` (hash prefix)
|
|
22
|
+
* - Vite production builds: `[name]-[hash][ext]` (hash suffix)
|
|
23
|
+
*
|
|
24
|
+
* We match every `-`-delimited prefix and suffix of the basename against the
|
|
25
|
+
* allowlist so both forms resolve (e.g. `arrow-right` from
|
|
26
|
+
* `a1b2c3d4-arrow-right` or `arrow-right-a1b2c3d4`). Returns `false` when the
|
|
27
|
+
* icon cannot be identified.
|
|
28
|
+
*/
|
|
29
|
+
export declare function shouldMirrorIconInRtl(icon: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves whether an icon is mirrored in RTL, honoring the `mirrorInRtl`
|
|
32
|
+
* override.
|
|
33
|
+
*
|
|
34
|
+
* `mirrorInRtl` wins in both directions when set: `true` opts a custom
|
|
35
|
+
* directional SVG in, `false` opts an allowlisted icon out. Omitting it falls
|
|
36
|
+
* back to the allowlist.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveMirrorInRtl(icon: string, mirrorInRtl?: boolean): boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon names mirrored in RTL by `PhosphorIcon`. Single source of truth —
|
|
3
|
+
* add or remove names in a Wonder Blocks PR rather than special-casing a call
|
|
4
|
+
* site. `mirrorInRtl` exists for the exceptions. See the PhosphorIcon RTL docs
|
|
5
|
+
* for deliberate exclusions (media, launch, trends, physical text-align, etc.).
|
|
6
|
+
*
|
|
7
|
+
* Names are the Phosphor asset names, without the weight suffix — browse them
|
|
8
|
+
* at https://phosphoricons.com/. All weights of a name share one entry, so
|
|
9
|
+
* `caret-right` covers `caret-right-bold`, `caret-right-fill`, and so on.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MIRRORED_ICON_NAMES: ReadonlySet<string>;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "6.0.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@khanacademy/wonder-blocks-core": "12.4.4",
|
|
24
|
-
"@khanacademy/wonder-blocks-tokens": "17.
|
|
24
|
+
"@khanacademy/wonder-blocks-tokens": "17.3.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@phosphor-icons/core": "^2.0.2",
|