feat(many): default components to Phosphor Icons for ionic theme (#29617)

Issue number: internal

---------

## What is the current behavior?
Icons in the Ionic theme default to Ionicons.

## What is the new behavior?
- Renames the following config properties:
  - `datetimeShowMonthYearIcon` to `datetimeExpandedIcon`
  - `datetimeHideMonthYearIcon` to `datetimeCollapsedIcon`
  - `selectExpandIcon` to `selectExpandedIcon`
- Updates the icons for the `ionic` theme to the following Phosphor
Icons:

| Component | Config | Phosphor Icon | Font Weight |
| ------------------------ | --------------------------- |
----------------- | ----------- |
| Accordion | `accordionToggleIcon` | `CaretDown` | `regular` |
| Back Button | `backButtonIcon` | `CaretLeft` | `regular` |
| Breadcrumb | `breadcrumbSeparatorIcon` | `CaretRight` | `regular` |
| Breadcrumb | `breadcrumbCollapsedIcon` | `DotsThree` | `regular` |
| Checkbox | N/A | `Check` | `bold` |
| Checkbox (indeterminate) | N/A | `Minus` | `bold` |
| Datetime | `datetimeNextIcon` | `CaretRight` | `regular` |
| Datetime | `datetimePreviousIcon` | `CaretLeft` | `regular` |
| Datetime | `datetimeCollapsedIcon` | `CaretDown` | `regular` |
| Datetime | `datetimeExpandedIcon` | `CaretUp` | `regular` |
| Fab Button | `fabButtonCloseIcon` | `X` | `regular` |
| Input | `inputClearIcon` | `X` | `regular` |
| Input Password Toggle | `inputPasswordShowIcon` | `Eye` | `regular` |
| Input Password Toggle | `inputPasswordHideIcon` | `EyeSlash` |
`regular` |
| Item | `itemDetailIcon` | `CaretRight` | `regular` |
| Menu Button | `menuIcon` | `List` | `regular` |
| Reorder | `reorderHandleIcon` | `List` | `regular` |
| Refresher | `refreshingIcon` |  | |
| Refresher (arrow icon) | N/A | `CaretLeft` | `fill` |
| Searchbar | `searchbarCancelIcon` | `ArrowLeft` | `regular` |
| Searchbar | `searchbarClearIcon` | `X` | `regular` |
| Searchbar | `searchbarSearchIcon` | `MagnifyingGlass` | `regular` |
| Select | `selectExpandedIcon` | `CaretDown` | `regular` |
| Select | `selectCollapsedIcon` | `CaretDown` | `regular` |
| Toggle | `toggleCheckedIcon` | `LineVertical` | `regular` |
| Toggle | `toggleUncheckedIcon` | `Circle` | `regular` |

## Does this introduce a breaking change?

- [ ] Yes
- [x] No
This commit is contained in:
Brandy Carney
2024-06-27 17:53:51 -04:00
committed by GitHub
parent 2a52942a6f
commit 08fc0b9160
63 changed files with 465 additions and 129 deletions

11
core/package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "8.2.2",
"license": "MIT",
"dependencies": {
"@phosphor-icons/core": "^2.1.1",
"@stencil/core": "^4.17.2",
"ionicons": "^7.2.2",
"tslib": "^2.1.0"
@ -1791,6 +1792,11 @@
"node": ">= 8"
}
},
"node_modules/@phosphor-icons/core": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@phosphor-icons/core/-/core-2.1.1.tgz",
"integrity": "sha512-v4ARvrip4qBCImOE5rmPUylOEK4iiED9ZyKjcvzuezqMaiRASCHKcRIuvvxL/twvLpkfnEODCOJp5dM4eZilxQ=="
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -12073,6 +12079,11 @@
"fastq": "^1.6.0"
}
},
"@phosphor-icons/core": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@phosphor-icons/core/-/core-2.1.1.tgz",
"integrity": "sha512-v4ARvrip4qBCImOE5rmPUylOEK4iiED9ZyKjcvzuezqMaiRASCHKcRIuvvxL/twvLpkfnEODCOJp5dM4eZilxQ=="
},
"@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",

View File

@ -31,6 +31,7 @@
"loader/"
],
"dependencies": {
"@phosphor-icons/core": "^2.1.1",
"@stencil/core": "^4.17.2",
"ionicons": "^7.2.2",
"tslib": "^2.1.0"

View File

@ -1,3 +1,4 @@
import caretDownRegular from '@phosphor-icons/core/assets/regular/caret-down.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, State, Watch, h } from '@stencil/core';
import { addEventListener, getElementRoot, raf, removeEventListener, transitionEndAsync } from '@utils/helpers';
@ -187,14 +188,34 @@ export class Accordion implements ComponentInterface {
button.setAttribute('aria-expanded', `${expanded}`);
};
get accordionToggleIcon() {
// Return the icon if it is explicitly set
if (this.toggleIcon != null) {
return this.toggleIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronDown,
ionic: caretDownRegular,
md: chevronDown,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured accordion toggle icon or the default icon
return config.get('accordionToggleIcon', defaultIcon);
}
private slotToggleIcon = () => {
const ionItem = this.getSlottedHeaderIonItem();
if (!ionItem) {
return;
}
const { toggleIconSlot } = this;
const accordionToggleIcon = this.toggleIcon ?? config.get('accordionToggleIcon', chevronDown);
const { accordionToggleIcon, toggleIconSlot } = this;
/**
* Check if there already is a toggle icon.

View File

@ -1,3 +1,4 @@
import caretLeftRegular from '@phosphor-icons/core/assets/regular/caret-left.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import type { ButtonInterface } from '@utils/element-interface';
@ -80,19 +81,24 @@ export class BackButton implements ComponentInterface, ButtonInterface {
}
get backButtonIcon() {
const icon = this.icon;
if (icon != null) {
// Icon is set on the component or by the config.
return icon;
// Return the icon if it is explicitly set
if (this.icon != null) {
return this.icon;
}
if (getIonTheme(this) === 'ios') {
// default ios back button icon
return config.get('backButtonIcon', chevronBack);
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronBack,
ionic: caretLeftRegular,
md: arrowBackSharp,
};
// default md back button icon
return config.get('backButtonIcon', arrowBackSharp);
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured back button icon or the default icon
return config.get('backButtonIcon', defaultIcon);
}
get backButtonText() {

View File

@ -1,3 +1,5 @@
import caretRightRegular from '@phosphor-icons/core/assets/regular/caret-right.svg';
import dotsThreeRegular from '@phosphor-icons/core/assets/regular/dots-three.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
import type { Attributes } from '@utils/helpers';
@ -133,6 +135,38 @@ export class Breadcrumb implements ComponentInterface {
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
get breadcrumbCollapsedIcon() {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: ellipsisHorizontal,
ionic: dotsThreeRegular,
md: ellipsisHorizontal,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured breadcrumb collapsed icon or the default icon
return config.get('breadcrumbCollapsedIcon', defaultIcon);
}
get breadcrumbSeparatorIcon() {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronForwardOutline,
ionic: caretRightRegular,
md: chevronForwardOutline,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured breadcrumb separator icon or the default icon
return config.get('breadcrumbSeparatorIcon', defaultIcon);
}
private isClickable(): boolean {
return this.href !== undefined;
}
@ -153,6 +187,8 @@ export class Breadcrumb implements ComponentInterface {
const {
color,
active,
breadcrumbCollapsedIcon,
breadcrumbSeparatorIcon,
collapsed,
disabled,
download,
@ -168,9 +204,6 @@ export class Breadcrumb implements ComponentInterface {
const clickable = this.isClickable();
const TagType = this.href === undefined ? 'span' : ('a' as any);
const breadcrumbSeparatorIcon = config.get('breadcrumbSeparatorIcon', chevronForwardOutline);
const breadcrumbCollapsedIcon = config.get('breadcrumbCollapsedIcon', ellipsisHorizontal);
// Links can still be tabbed to when set to disabled if they have an href
// in order to truly disable them we can keep it as an anchor but remove the href
const href = disabled ? undefined : this.href;
@ -239,7 +272,7 @@ export class Breadcrumb implements ComponentInterface {
*/
<span class="breadcrumb-separator" part="separator" aria-hidden="true">
<slot name="separator">
{theme === 'ios' ? (
{theme === 'ios' || theme === 'ionic' ? (
<ion-icon icon={breadcrumbSeparatorIcon} lazy={false} flip-rtl></ion-icon>
) : (
<span>/</span>

View File

@ -149,9 +149,7 @@ input {
}
.checkbox-icon path {
fill: none;
stroke: var(--checkmark-color);
stroke-width: var(--checkmark-width);
fill: var(--checkmark-color);
opacity: 0;
}

View File

@ -244,7 +244,8 @@ export class Checkbox implements ComponentInterface {
<slot></slot>
</div>
<div class="native-wrapper">
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
{/* Phosphor Icons define a larger viewBox */}
<svg class="checkbox-icon" viewBox={theme === 'ionic' ? '0 0 256 256' : '0 0 24 24'} part="container">
{path}
</svg>
</div>
@ -268,9 +269,11 @@ export class Checkbox implements ComponentInterface {
);
} else if (theme === 'ionic') {
path = indeterminate ? (
<path d="M6.5 12H17.5" stroke-linecap="round" part="mark" />
// Phosphor Icon - minus bold
<path d="M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z"></path>
) : (
<path d="M6 12.5L10 16.5L18.5 8" stroke-linecap="round" stroke-linejoin="round" part="mark" />
// Phosphor Icon - check bold
<path d="M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z"></path>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,3 +1,7 @@
import caretDownRegular from '@phosphor-icons/core/assets/regular/caret-down.svg';
import caretLeftRegular from '@phosphor-icons/core/assets/regular/caret-left.svg';
import caretRightRegular from '@phosphor-icons/core/assets/regular/caret-right.svg';
import caretUpRegular from '@phosphor-icons/core/assets/regular/caret-up.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTask } from '@stencil/core';
import { startFocusVisible } from '@utils/focus-visible';
@ -2116,10 +2120,7 @@ export class Datetime implements ComponentInterface {
*/
private renderCalendarHeader(theme: Theme) {
const { disabled, datetimeShowMonthYearIcon, datetimeHideMonthYearIcon } = this;
const datetimeNextIcon = config.get('datetimeNextIcon', chevronForward);
const datetimePrevIcon = config.get('datetimePreviousIcon', chevronBack);
const { disabled, datetimeNextIcon, datetimePreviousIcon, datetimeCollapsedIcon, datetimeExpandedIcon } = this;
const prevMonthDisabled = disabled || isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
const nextMonthDisabled = disabled || isNextMonthDisabled(this.workingParts, this.maxParts);
@ -2146,7 +2147,7 @@ export class Datetime implements ComponentInterface {
{getMonthAndYear(this.locale, this.workingParts)}
<ion-icon
aria-hidden="true"
icon={this.showMonthAndYear ? datetimeShowMonthYearIcon : datetimeHideMonthYearIcon}
icon={this.showMonthAndYear ? datetimeExpandedIcon : datetimeCollapsedIcon}
lazy={false}
flipRtl={true}
></ion-icon>
@ -2162,7 +2163,7 @@ export class Datetime implements ComponentInterface {
dir={hostDir}
aria-hidden="true"
slot="icon-only"
icon={datetimePrevIcon}
icon={datetimePreviousIcon}
lazy={false}
flipRtl
></ion-icon>
@ -2595,32 +2596,88 @@ export class Datetime implements ComponentInterface {
}
}
/**
* Get the icon to use for the next icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*/
get datetimeNextIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronForward,
ionic: caretRightRegular,
md: chevronForward,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured datetime next icon or the default icon
return config.get('datetimeNextIcon', defaultIcon);
}
/**
* Get the icon to use for the previous icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*/
get datetimePreviousIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronBack,
ionic: caretLeftRegular,
md: chevronBack,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured datetime previous icon or the default icon
return config.get('datetimePreviousIcon', defaultIcon);
}
/**
* Get the icon to use for the show month and year icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the show month and year icon.
*/
get datetimeShowMonthYearIcon(): string {
get datetimeCollapsedIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const expandedIcon = theme === 'ios' ? chevronDown : caretUpSharp;
const defaultIcons = {
ios: chevronForward,
ionic: caretDownRegular,
md: caretDownSharp,
};
return config.get('datetimeShowMonthYearIcon', expandedIcon);
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured datetime show month and year icon or the default icon
return config.get('datetimeCollapsedIcon', defaultIcon);
}
/**
* Get the icon to use for the hide month and year icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the hide month and year icon.
*/
get datetimeHideMonthYearIcon(): string {
get datetimeExpandedIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const collapsedIcon = theme === 'ios' ? chevronForward : caretDownSharp;
const defaultIcons = {
ios: chevronDown,
ionic: caretUpRegular,
md: caretUpSharp,
};
return config.get('datetimeHideMonthYearIcon', collapsedIcon);
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured datetime hide month and year icon or the default icon
return config.get('datetimeExpandedIcon', defaultIcon);
}
render() {

View File

@ -1,3 +1,4 @@
import xRegular from '@phosphor-icons/core/assets/regular/x.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
@ -153,8 +154,30 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
get fabButtonCloseIcon() {
// Return the icon if it is explicitly set
if (this.closeIcon != null) {
return this.closeIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: close,
ionic: xRegular,
md: close,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured fab button close icon or the default icon
return config.get('fabButtonCloseIcon', defaultIcon);
}
render() {
const { el, disabled, color, href, activated, show, translucent, size, inheritedAttributes } = this;
const { el, disabled, fabButtonCloseIcon, color, href, activated, show, translucent, size, inheritedAttributes } =
this;
const inList = hostContext('ion-fab-list', el);
const theme = getIonTheme(this);
const TagType = href === undefined ? 'button' : ('a' as any);
@ -167,7 +190,6 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
rel: this.rel,
target: this.target,
};
const fabButtonCloseIcon = this.closeIcon ?? config.get('fabButtonCloseIcon', close);
return (
<Host

View File

@ -1,3 +1,5 @@
import eyeSlashRegular from '@phosphor-icons/core/assets/regular/eye-slash.svg';
import eyeRegular from '@phosphor-icons/core/assets/regular/eye.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h, Watch } from '@stencil/core';
import { printIonWarning } from '@utils/logging';
@ -5,7 +7,7 @@ import { createColorClasses } from '@utils/theme';
import { eyeOff, eye } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { getIonMode, getIonTheme } from '../../global/ionic-global';
import type { Color, TextFieldTypes } from '../../interface';
/**
@ -106,14 +108,54 @@ export class InputPasswordToggle implements ComponentInterface {
inputElRef.type = inputElRef.type === 'text' ? 'password' : 'text';
};
get inputPasswordHideIcon() {
// Return the icon if it is explicitly set
if (this.hideIcon != null) {
return this.hideIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: eyeOff,
ionic: eyeSlashRegular,
md: eyeOff,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured input password hide icon or the default icon
return config.get('inputPasswordHideIcon', defaultIcon);
}
get inputPasswordShowIcon() {
// Return the icon if it is explicitly set
if (this.showIcon != null) {
return this.showIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: eye,
ionic: eyeRegular,
md: eye,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured input password show icon or the default icon
return config.get('inputPasswordShowIcon', defaultIcon);
}
render() {
const { color, type } = this;
const { color, inputPasswordHideIcon, inputPasswordShowIcon, type } = this;
const mode = getIonMode(this);
const isPasswordVisible = type === 'text';
const inputPasswordShowIcon = this.showIcon ?? config.get('inputPasswordShowIcon', eye);
const inputPasswordHideIcon = this.hideIcon ?? config.get('inputPasswordHideIcon', eyeOff);
return (
<Host

View File

@ -1,3 +1,4 @@
import xRegular from '@phosphor-icons/core/assets/regular/x.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import type { NotchController } from '@utils/forms';
@ -766,19 +767,26 @@ export class Input implements ComponentInterface {
* If no icon is set in the config, use the default icon.
*
* @internal
* @returns {string} The icon to use for the clear icon.
*/
get inputClearIcon(): string {
const theme = getIonTheme(this);
const defaultClearIcon = theme === 'ios' ? closeCircle : closeSharp;
const icon = this.clearInputIcon;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
// Return the icon if it is explicitly set
if (this.clearInputIcon != null) {
return this.clearInputIcon;
}
return config.get('inputClearIcon', defaultClearIcon);
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: closeCircle,
ionic: xRegular,
md: closeSharp,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured input clear icon or the default icon
return config.get('inputClearIcon', defaultIcon);
}
render() {

View File

@ -50,6 +50,10 @@
<h2>Default</h2>
<ion-input value="hi@ionic.io" label="Email"></ion-input>
</div>
<div class="grid-item">
<h2>Clear Input</h2>
<ion-input value="hi@ionic.io" clear-input="true" label="Email"></ion-input>
</div>
</div>
</ion-content>
</ion-app>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,3 +1,4 @@
import caretRightRegular from '@phosphor-icons/core/assets/regular/caret-right.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Listen, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
@ -242,6 +243,27 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
return controls[0];
}
get itemDetailIcon() {
// Return the icon if it is explicitly set
if (this.detailIcon != null) {
return this.detailIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronForward,
ionic: caretRightRegular,
md: chevronForward,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured item detail icon or the default icon
return config.get('itemDetailIcon', defaultIcon);
}
render() {
const {
detail,
@ -250,6 +272,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
lines,
disabled,
href,
itemDetailIcon,
rel,
target,
routerAnimation,
@ -262,7 +285,6 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
const clickable = this.isClickable();
const canActivate = this.canActivate();
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
const itemDetailIcon = this.detailIcon ?? config.get('itemDetailIcon', chevronForward);
const attrs =
TagType === 'button'

View File

@ -1,3 +1,4 @@
import listRegular from '@phosphor-icons/core/assets/regular/list.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Listen, Prop, State, h } from '@stencil/core';
import type { ButtonInterface } from '@utils/element-interface';
@ -76,14 +77,29 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
this.visible = await updateVisibility(this.menu);
}
get menuIcon() {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: menuOutline,
ionic: listRegular,
md: menuSharp,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured menu icon or the default icon
return config.get('menuIcon', defaultIcon);
}
private onClick = async () => {
return menuController.toggle(this.menu);
};
render() {
const { color, disabled, inheritedAttributes } = this;
const { color, disabled, inheritedAttributes, menuIcon } = this;
const theme = getIonTheme(this);
const menuIcon = config.get('menuIcon', theme === 'ios' ? menuOutline : menuSharp);
const hidden = this.autoHide && !this.visible;
const attrs = {

View File

@ -0,0 +1 @@
// Empty file in order to properly apply theme

View File

@ -1,3 +1,4 @@
import caretLeftFill from '@phosphor-icons/core/assets/fill/caret-left-fill.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config';
@ -17,6 +18,11 @@ import { SPINNERS } from '../spinner/spinner-configs';
*/
@Component({
tag: 'ion-refresher-content',
styleUrls: {
ios: 'refresher-content.scss',
md: 'refresher-content.scss',
ionic: 'refresher-content.scss',
},
})
export class RefresherContent implements ComponentInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
@ -111,6 +117,7 @@ export class RefresherContent implements ComponentInterface {
const pullingIcon = this.pullingIcon;
const hasSpinner = pullingIcon != null && (SPINNERS[pullingIcon] as any) !== undefined;
const theme = getIonTheme(this);
const arrowIcon = theme === 'ionic' ? caretLeftFill : caretBackSharp;
return (
<Host
@ -123,9 +130,9 @@ export class RefresherContent implements ComponentInterface {
<div class="refresher-pulling-icon">
<div class="spinner-arrow-container">
<ion-spinner name={this.pullingIcon as SpinnerTypes} paused></ion-spinner>
{theme === 'md' && this.pullingIcon === 'circular' && (
{(theme === 'md' || theme === 'ionic') && this.pullingIcon === 'circular' && (
<div class="arrow-container">
<ion-icon icon={caretBackSharp} aria-hidden="true"></ion-icon>
<ion-icon icon={arrowIcon} aria-hidden="true"></ion-icon>
</div>
)}
</div>

View File

@ -1,3 +1,4 @@
import listRegular from '@phosphor-icons/core/assets/regular/list.svg';
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Listen, h } from '@stencil/core';
import { reorderThreeOutline, reorderTwoSharp } from 'ionicons/icons';
@ -40,14 +41,21 @@ export class Reorder implements ComponentInterface {
* Get the icon to use for the handle icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the handle icon.
*/
get reorderHandleIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const reorderIcon = theme === 'ios' ? reorderThreeOutline : reorderTwoSharp;
const defaultIcons = {
ios: reorderThreeOutline,
ionic: listRegular,
md: reorderTwoSharp,
};
return config.get('reorderHandleIcon', reorderIcon);
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured reorder handle icon or the default icon
return config.get('reorderHandleIcon', defaultIcon);
}
render() {

View File

@ -1,3 +1,6 @@
import arrowLeftRegular from '@phosphor-icons/core/assets/regular/arrow-left.svg';
import magnifyingGlassRegular from '@phosphor-icons/core/assets/regular/magnifying-glass.svg';
import xRegular from '@phosphor-icons/core/assets/regular/x.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import { debounceEvent, raf, componentOnReady, inheritAttributes } from '@utils/helpers';
@ -614,19 +617,25 @@ export class Searchbar implements ComponentInterface {
* If an icon is set on the component, use that.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the clear icon.
*/
get searchbarClearIcon(): string {
const theme = getIonTheme(this);
const icon = this.clearIcon;
const defaultIcon = theme === 'ios' ? closeCircle : closeSharp;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
// Return the icon if it is explicitly set
if (this.clearIcon != null) {
return this.clearIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: closeCircle,
ionic: xRegular,
md: closeSharp,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured searchbar clear icon or the default icon
return config.get('searchbarClearIcon', defaultIcon);
}
@ -635,35 +644,60 @@ export class Searchbar implements ComponentInterface {
* If an icon is set on the component, use that.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the search icon.
*/
get searchbarSearchIcon(): string {
const theme = getIonTheme(this);
const icon = this.searchIcon;
const defaultIcon = theme === 'ios' ? searchOutline : searchSharp;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
// Return the icon if it is explicitly set
if (this.searchIcon != null) {
return this.searchIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: searchOutline,
ionic: magnifyingGlassRegular,
md: searchSharp,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured searchbar search icon or the default icon
return config.get('searchbarSearchIcon', defaultIcon);
}
/**
* Get the icon to use for the cancel icon.
* If an icon is set on the component, use that.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*/
get searchbarCancelIcon(): string {
// Return the icon if it is explicitly set
if (this.cancelButtonIcon != null) {
return this.cancelButtonIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: arrowBackSharp,
ionic: arrowLeftRegular,
md: arrowBackSharp,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured searchbar cancel icon, the back button icon or the default icon
return config.get('searchbarCancelIcon', config.get('backButtonIcon', defaultIcon));
}
render() {
const { cancelButtonText, autocapitalize, searchbarClearIcon, searchbarSearchIcon } = this;
const { cancelButtonText, autocapitalize, searchbarCancelIcon, searchbarClearIcon, searchbarSearchIcon } = this;
const animated = this.animated && config.getBoolean('animated', true);
const theme = getIonTheme(this);
const shouldShowCancelButton = this.shouldShowCancelButton();
/**
* The `backButtonIcon` config will be used as a fallback if the
* `searchbarCancelIcon` config is not set. This ensures apps
* have a way to keep the back button icon and searchbar cancel
* icon in sync.
*/
const searchbarCancelIcon =
this.cancelButtonIcon ?? config.get('searchbarCancelIcon', config.get('backButtonIcon', arrowBackSharp));
const cancelButton = this.showCancelButton !== 'never' && (
<button
@ -677,7 +711,7 @@ export class Searchbar implements ComponentInterface {
class="searchbar-cancel-button"
>
<div aria-hidden="true">
{theme === 'md' ? (
{theme === 'md' || theme === 'ionic' ? (
<ion-icon aria-hidden="true" icon={searchbarCancelIcon} lazy={false}></ion-icon>
) : (
cancelButtonText
@ -727,7 +761,7 @@ export class Searchbar implements ComponentInterface {
{...this.inheritedAttributes}
/>
{theme === 'md' && cancelButton}
{(theme === 'md' || theme === 'ionic') && cancelButton}
<ion-icon aria-hidden="true" icon={searchbarSearchIcon} lazy={false} class="searchbar-search-icon"></ion-icon>

View File

@ -1,3 +1,4 @@
import caretDownRegular from '@phosphor-icons/core/assets/regular/caret-down.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, h, forceUpdate } from '@stencil/core';
import type { NotchController } from '@utils/forms';
@ -878,11 +879,11 @@ export class Select implements ComponentInterface {
* next to the select text.
*/
private renderSelectIcon() {
const { isExpanded, selectExpandIcon, selectCollapsedIcon } = this;
const { isExpanded, selectExpandedIcon, selectCollapsedIcon } = this;
let icon = selectCollapsedIcon;
if (isExpanded) {
icon = selectExpandIcon;
icon = selectExpandedIcon;
}
return <ion-icon class="select-icon" part="icon" aria-hidden="true" icon={icon}></ion-icon>;
@ -941,25 +942,28 @@ export class Select implements ComponentInterface {
* If an icon is set on the component, use that.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the expand icon.
*/
get selectExpandIcon(): string {
get selectExpandedIcon(): string {
// Return the expandedIcon or toggleIcon if either is explicitly set
if (this.expandedIcon != null) {
return this.expandedIcon;
} else if (this.toggleIcon != null) {
return this.toggleIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const icon = this.expandedIcon;
let defaultExpandIcon = theme === 'ios' ? chevronExpand : caretDownSharp;
const defaultIcons = {
ios: chevronExpand,
ionic: caretDownRegular,
md: caretDownSharp,
};
if (icon !== undefined) {
// Icon is set on the component.
return icon;
}
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
if (this.toggleIcon) {
// If the toggleIcon is set, use that as the default expand icon.
defaultExpandIcon = this.toggleIcon;
}
return config.get('selectExpandIcon', defaultExpandIcon);
// Return the configured select expanded icon or the default icon
return config.get('selectExpandedIcon', defaultIcon);
}
/**
@ -967,19 +971,24 @@ export class Select implements ComponentInterface {
* If an icon is set on the component, use that.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the collapsed icon.
*/
get selectCollapsedIcon(): string {
const theme = getIonTheme(this);
const icon = this.toggleIcon;
const defaultIcon = theme === 'ios' ? chevronExpand : caretDownSharp;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
// Return the toggleIcon if it is explicitly set
if (this.toggleIcon) {
return this.toggleIcon;
}
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronExpand,
ionic: caretDownRegular,
md: caretDownSharp,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
return config.get('selectCollapsedIcon', defaultIcon);
}

View File

@ -1,3 +1,5 @@
import circleRegular from '@phosphor-icons/core/assets/regular/circle.svg';
import lineVerticalRegular from '@phosphor-icons/core/assets/regular/line-vertical.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, State, Watch, h } from '@stencil/core';
import { renderHiddenInput, inheritAriaAttributes } from '@utils/helpers';
@ -248,16 +250,25 @@ export class Toggle implements ComponentInterface {
};
get toggleDefaultCheckedIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
return theme === 'md' ? checkmarkOutline : removeOutline;
const defaultIcons = {
ios: removeOutline,
ionic: lineVerticalRegular,
md: checkmarkOutline,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the default icon
return defaultIcon;
}
/**
* Get the icon to use for the checked icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the checked icon.
*/
get toggleCheckedIcon(): string {
return config.get('toggleCheckedIcon', this.toggleDefaultCheckedIcon);
@ -267,13 +278,20 @@ export class Toggle implements ComponentInterface {
* Get the icon to use for the unchecked icon.
* Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon.
*
* @returns {string} The icon to use for the unchecked icon.
*/
get toggleUncheckedIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcon = theme === 'md' ? removeOutline : ellipseOutline;
const defaultIcons = {
ios: ellipseOutline,
ionic: circleRegular,
md: removeOutline,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured toggle unchecked icon or the default icon
return config.get('toggleUncheckedIcon', defaultIcon);
}
@ -316,7 +334,8 @@ export class Toggle implements ComponentInterface {
theme === 'ios' && [this.renderOnOffSwitchLabels(true), this.renderOnOffSwitchLabels(false)]}
<div class="toggle-icon-wrapper">
<div class="toggle-inner" part="handle">
{enableOnOffLabels && theme === 'md' && this.renderOnOffSwitchLabels(checked)}
{/* TODO(ROU-10830): The ionic theme will need to be moved up with ios when the design is implemented */}
{enableOnOffLabels && (theme === 'md' || theme === 'ionic') && this.renderOnOffSwitchLabels(checked)}
</div>
</div>
</div>

View File

@ -151,14 +151,14 @@ export interface IonicConfig {
datetimePreviousIcon?: string;
/**
* Overrides the show month and year icon for all `ion-datetime` components.
* Overrides the expanded icon for all `ion-datetime` components.
*/
datetimeShowMonthYearIcon?: string;
datetimeExpandedIcon?: string;
/**
* Overrides the hide month and year icon for all `ion-datetime` components.
* Overrides the collapsed icon for all `ion-datetime` components.
*/
datetimeHideMonthYearIcon?: string;
datetimeCollapsedIcon?: string;
/**
* Overrides the close icon for all `ion-fab-button` components.
@ -209,7 +209,7 @@ export interface IonicConfig {
/**
* Overrides the expand icon for all `ion-select` components.
*/
selectExpandIcon?: string;
selectExpandedIcon?: string;
/**
* Overrides the collapsed icon for all `ion-select` components.

View File

@ -250,6 +250,7 @@ export const config: Config = {
moduleNameMapper: {
"@utils/test": ["<rootDir>/src/utils/test/utils"],
"@utils/logging": ["<rootDir>/src/utils/logging"],
"^.+\\.svg": "<rootDir>/svgTransform.js"
},
setupFilesAfterEnv: ['./setupJest.js']
},

13
core/svgTransform.js Normal file
View File

@ -0,0 +1,13 @@
// This transform is required for svg files to work with Jest
// See https://stackoverflow.com/q/46791263/3802466
module.exports = {
process() {
return {
code: `module.exports = {};`,
};
},
getCacheKey() {
// The output is always the same.
return "svgTransform";
},
};