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

View File

@ -31,6 +31,7 @@
"loader/" "loader/"
], ],
"dependencies": { "dependencies": {
"@phosphor-icons/core": "^2.1.1",
"@stencil/core": "^4.17.2", "@stencil/core": "^4.17.2",
"ionicons": "^7.2.2", "ionicons": "^7.2.2",
"tslib": "^2.1.0" "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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, State, Watch, h } from '@stencil/core'; import { Component, Element, Host, Prop, State, Watch, h } from '@stencil/core';
import { addEventListener, getElementRoot, raf, removeEventListener, transitionEndAsync } from '@utils/helpers'; import { addEventListener, getElementRoot, raf, removeEventListener, transitionEndAsync } from '@utils/helpers';
@ -187,14 +188,34 @@ export class Accordion implements ComponentInterface {
button.setAttribute('aria-expanded', `${expanded}`); 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 = () => { private slotToggleIcon = () => {
const ionItem = this.getSlottedHeaderIonItem(); const ionItem = this.getSlottedHeaderIonItem();
if (!ionItem) { if (!ionItem) {
return; return;
} }
const { toggleIconSlot } = this; const { accordionToggleIcon, toggleIconSlot } = this;
const accordionToggleIcon = this.toggleIcon ?? config.get('accordionToggleIcon', chevronDown);
/** /**
* Check if there already is a toggle icon. * 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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core'; import { Component, Element, Host, Prop, h } from '@stencil/core';
import type { ButtonInterface } from '@utils/element-interface'; import type { ButtonInterface } from '@utils/element-interface';
@ -80,19 +81,24 @@ export class BackButton implements ComponentInterface, ButtonInterface {
} }
get backButtonIcon() { get backButtonIcon() {
const icon = this.icon; // Return the icon if it is explicitly set
if (icon != null) { if (this.icon != null) {
// Icon is set on the component or by the config. return this.icon;
return icon;
} }
if (getIonTheme(this) === 'ios') { // Determine the theme and map to default icons
// default ios back button icon const theme = getIonTheme(this);
return config.get('backButtonIcon', chevronBack); const defaultIcons = {
} ios: chevronBack,
ionic: caretLeftRegular,
md: arrowBackSharp,
};
// default md back button icon // Get the default icon based on the theme, falling back to 'md' icon if necessary
return config.get('backButtonIcon', arrowBackSharp); const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured back button icon or the default icon
return config.get('backButtonIcon', defaultIcon);
} }
get backButtonText() { 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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, h } from '@stencil/core'; import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
import type { Attributes } from '@utils/helpers'; import type { Attributes } from '@utils/helpers';
@ -133,6 +135,38 @@ export class Breadcrumb implements ComponentInterface {
this.inheritedAttributes = inheritAriaAttributes(this.el); 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 { private isClickable(): boolean {
return this.href !== undefined; return this.href !== undefined;
} }
@ -153,6 +187,8 @@ export class Breadcrumb implements ComponentInterface {
const { const {
color, color,
active, active,
breadcrumbCollapsedIcon,
breadcrumbSeparatorIcon,
collapsed, collapsed,
disabled, disabled,
download, download,
@ -168,9 +204,6 @@ export class Breadcrumb implements ComponentInterface {
const clickable = this.isClickable(); const clickable = this.isClickable();
const TagType = this.href === undefined ? 'span' : ('a' as any); 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 // 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 // in order to truly disable them we can keep it as an anchor but remove the href
const href = disabled ? undefined : this.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"> <span class="breadcrumb-separator" part="separator" aria-hidden="true">
<slot name="separator"> <slot name="separator">
{theme === 'ios' ? ( {theme === 'ios' || theme === 'ionic' ? (
<ion-icon icon={breadcrumbSeparatorIcon} lazy={false} flip-rtl></ion-icon> <ion-icon icon={breadcrumbSeparatorIcon} lazy={false} flip-rtl></ion-icon>
) : ( ) : (
<span>/</span> <span>/</span>

View File

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

View File

@ -244,7 +244,8 @@ export class Checkbox implements ComponentInterface {
<slot></slot> <slot></slot>
</div> </div>
<div class="native-wrapper"> <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} {path}
</svg> </svg>
</div> </div>
@ -268,9 +269,11 @@ export class Checkbox implements ComponentInterface {
); );
} else if (theme === 'ionic') { } else if (theme === 'ionic') {
path = indeterminate ? ( 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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTask } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTask } from '@stencil/core';
import { startFocusVisible } from '@utils/focus-visible'; import { startFocusVisible } from '@utils/focus-visible';
@ -2116,10 +2120,7 @@ export class Datetime implements ComponentInterface {
*/ */
private renderCalendarHeader(theme: Theme) { private renderCalendarHeader(theme: Theme) {
const { disabled, datetimeShowMonthYearIcon, datetimeHideMonthYearIcon } = this; const { disabled, datetimeNextIcon, datetimePreviousIcon, datetimeCollapsedIcon, datetimeExpandedIcon } = this;
const datetimeNextIcon = config.get('datetimeNextIcon', chevronForward);
const datetimePrevIcon = config.get('datetimePreviousIcon', chevronBack);
const prevMonthDisabled = disabled || isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts); const prevMonthDisabled = disabled || isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
const nextMonthDisabled = disabled || isNextMonthDisabled(this.workingParts, this.maxParts); const nextMonthDisabled = disabled || isNextMonthDisabled(this.workingParts, this.maxParts);
@ -2146,7 +2147,7 @@ export class Datetime implements ComponentInterface {
{getMonthAndYear(this.locale, this.workingParts)} {getMonthAndYear(this.locale, this.workingParts)}
<ion-icon <ion-icon
aria-hidden="true" aria-hidden="true"
icon={this.showMonthAndYear ? datetimeShowMonthYearIcon : datetimeHideMonthYearIcon} icon={this.showMonthAndYear ? datetimeExpandedIcon : datetimeCollapsedIcon}
lazy={false} lazy={false}
flipRtl={true} flipRtl={true}
></ion-icon> ></ion-icon>
@ -2162,7 +2163,7 @@ export class Datetime implements ComponentInterface {
dir={hostDir} dir={hostDir}
aria-hidden="true" aria-hidden="true"
slot="icon-only" slot="icon-only"
icon={datetimePrevIcon} icon={datetimePreviousIcon}
lazy={false} lazy={false}
flipRtl flipRtl
></ion-icon> ></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. * Get the icon to use for the show month and year icon.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 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. * Get the icon to use for the hide month and year icon.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 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() { 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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, h } from '@stencil/core'; import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface'; import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
@ -153,8 +154,30 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
this.inheritedAttributes = inheritAriaAttributes(this.el); 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() { 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 inList = hostContext('ion-fab-list', el);
const theme = getIonTheme(this); const theme = getIonTheme(this);
const TagType = href === undefined ? 'button' : ('a' as any); const TagType = href === undefined ? 'button' : ('a' as any);
@ -167,7 +190,6 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
rel: this.rel, rel: this.rel,
target: this.target, target: this.target,
}; };
const fabButtonCloseIcon = this.closeIcon ?? config.get('fabButtonCloseIcon', close);
return ( return (
<Host <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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h, Watch } from '@stencil/core'; import { Component, Element, Host, Prop, h, Watch } from '@stencil/core';
import { printIonWarning } from '@utils/logging'; import { printIonWarning } from '@utils/logging';
@ -5,7 +7,7 @@ import { createColorClasses } from '@utils/theme';
import { eyeOff, eye } from 'ionicons/icons'; import { eyeOff, eye } from 'ionicons/icons';
import { config } from '../../global/config'; import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global'; import { getIonMode, getIonTheme } from '../../global/ionic-global';
import type { Color, TextFieldTypes } from '../../interface'; import type { Color, TextFieldTypes } from '../../interface';
/** /**
@ -106,14 +108,54 @@ export class InputPasswordToggle implements ComponentInterface {
inputElRef.type = inputElRef.type === 'text' ? 'password' : 'text'; 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() { render() {
const { color, type } = this; const { color, inputPasswordHideIcon, inputPasswordShowIcon, type } = this;
const mode = getIonMode(this); const mode = getIonMode(this);
const isPasswordVisible = type === 'text'; const isPasswordVisible = type === 'text';
const inputPasswordShowIcon = this.showIcon ?? config.get('inputPasswordShowIcon', eye);
const inputPasswordHideIcon = this.hideIcon ?? config.get('inputPasswordHideIcon', eyeOff);
return ( return (
<Host <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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } from '@stencil/core'; import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import type { NotchController } from '@utils/forms'; 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. * If no icon is set in the config, use the default icon.
* *
* @internal * @internal
* @returns {string} The icon to use for the clear icon.
*/ */
get inputClearIcon(): string { get inputClearIcon(): string {
const theme = getIonTheme(this); // Return the icon if it is explicitly set
const defaultClearIcon = theme === 'ios' ? closeCircle : closeSharp; if (this.clearInputIcon != null) {
const icon = this.clearInputIcon; return this.clearInputIcon;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
} }
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() { render() {

View File

@ -50,6 +50,10 @@
<h2>Default</h2> <h2>Default</h2>
<ion-input value="hi@ionic.io" label="Email"></ion-input> <ion-input value="hi@ionic.io" label="Email"></ion-input>
</div> </div>
<div class="grid-item">
<h2>Clear Input</h2>
<ion-input value="hi@ionic.io" clear-input="true" label="Email"></ion-input>
</div>
</div> </div>
</ion-content> </ion-content>
</ion-app> </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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Listen, Prop, State, Watch, forceUpdate, h } from '@stencil/core'; import { Component, Element, Host, Listen, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface'; import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
@ -242,6 +243,27 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
return controls[0]; 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() { render() {
const { const {
detail, detail,
@ -250,6 +272,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
lines, lines,
disabled, disabled,
href, href,
itemDetailIcon,
rel, rel,
target, target,
routerAnimation, routerAnimation,
@ -262,7 +285,6 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
const clickable = this.isClickable(); const clickable = this.isClickable();
const canActivate = this.canActivate(); const canActivate = this.canActivate();
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any); const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
const itemDetailIcon = this.detailIcon ?? config.get('itemDetailIcon', chevronForward);
const attrs = const attrs =
TagType === 'button' 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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Listen, Prop, State, h } from '@stencil/core'; import { Component, Element, Host, Listen, Prop, State, h } from '@stencil/core';
import type { ButtonInterface } from '@utils/element-interface'; import type { ButtonInterface } from '@utils/element-interface';
@ -76,14 +77,29 @@ export class MenuButton implements ComponentInterface, ButtonInterface {
this.visible = await updateVisibility(this.menu); 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 () => { private onClick = async () => {
return menuController.toggle(this.menu); return menuController.toggle(this.menu);
}; };
render() { render() {
const { color, disabled, inheritedAttributes } = this; const { color, disabled, inheritedAttributes, menuIcon } = this;
const theme = getIonTheme(this); const theme = getIonTheme(this);
const menuIcon = config.get('menuIcon', theme === 'ios' ? menuOutline : menuSharp);
const hidden = this.autoHide && !this.visible; const hidden = this.autoHide && !this.visible;
const attrs = { 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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core'; import { Component, Element, Host, Prop, h } from '@stencil/core';
import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config';
@ -17,6 +18,11 @@ import { SPINNERS } from '../spinner/spinner-configs';
*/ */
@Component({ @Component({
tag: 'ion-refresher-content', tag: 'ion-refresher-content',
styleUrls: {
ios: 'refresher-content.scss',
md: 'refresher-content.scss',
ionic: 'refresher-content.scss',
},
}) })
export class RefresherContent implements ComponentInterface { export class RefresherContent implements ComponentInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT); private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
@ -111,6 +117,7 @@ export class RefresherContent implements ComponentInterface {
const pullingIcon = this.pullingIcon; const pullingIcon = this.pullingIcon;
const hasSpinner = pullingIcon != null && (SPINNERS[pullingIcon] as any) !== undefined; const hasSpinner = pullingIcon != null && (SPINNERS[pullingIcon] as any) !== undefined;
const theme = getIonTheme(this); const theme = getIonTheme(this);
const arrowIcon = theme === 'ionic' ? caretLeftFill : caretBackSharp;
return ( return (
<Host <Host
@ -123,9 +130,9 @@ export class RefresherContent implements ComponentInterface {
<div class="refresher-pulling-icon"> <div class="refresher-pulling-icon">
<div class="spinner-arrow-container"> <div class="spinner-arrow-container">
<ion-spinner name={this.pullingIcon as SpinnerTypes} paused></ion-spinner> <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"> <div class="arrow-container">
<ion-icon icon={caretBackSharp} aria-hidden="true"></ion-icon> <ion-icon icon={arrowIcon} aria-hidden="true"></ion-icon>
</div> </div>
)} )}
</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 type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Listen, h } from '@stencil/core'; import { Component, Element, Host, Listen, h } from '@stencil/core';
import { reorderThreeOutline, reorderTwoSharp } from 'ionicons/icons'; import { reorderThreeOutline, reorderTwoSharp } from 'ionicons/icons';
@ -40,14 +41,21 @@ export class Reorder implements ComponentInterface {
* Get the icon to use for the handle icon. * Get the icon to use for the handle icon.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 { get reorderHandleIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this); 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() { 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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } 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'; 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. * If an icon is set on the component, use that.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 { get searchbarClearIcon(): string {
const theme = getIonTheme(this); // Return the icon if it is explicitly set
const icon = this.clearIcon; if (this.clearIcon != null) {
const defaultIcon = theme === 'ios' ? closeCircle : closeSharp; return this.clearIcon;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
} }
// 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); return config.get('searchbarClearIcon', defaultIcon);
} }
@ -635,35 +644,60 @@ export class Searchbar implements ComponentInterface {
* If an icon is set on the component, use that. * If an icon is set on the component, use that.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 { get searchbarSearchIcon(): string {
const theme = getIonTheme(this); // Return the icon if it is explicitly set
const icon = this.searchIcon; if (this.searchIcon != null) {
const defaultIcon = theme === 'ios' ? searchOutline : searchSharp; return this.searchIcon;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
} }
// 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); 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() { render() {
const { cancelButtonText, autocapitalize, searchbarClearIcon, searchbarSearchIcon } = this; const { cancelButtonText, autocapitalize, searchbarCancelIcon, searchbarClearIcon, searchbarSearchIcon } = this;
const animated = this.animated && config.getBoolean('animated', true); const animated = this.animated && config.getBoolean('animated', true);
const theme = getIonTheme(this); const theme = getIonTheme(this);
const shouldShowCancelButton = this.shouldShowCancelButton(); 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' && ( const cancelButton = this.showCancelButton !== 'never' && (
<button <button
@ -677,7 +711,7 @@ export class Searchbar implements ComponentInterface {
class="searchbar-cancel-button" class="searchbar-cancel-button"
> >
<div aria-hidden="true"> <div aria-hidden="true">
{theme === 'md' ? ( {theme === 'md' || theme === 'ionic' ? (
<ion-icon aria-hidden="true" icon={searchbarCancelIcon} lazy={false}></ion-icon> <ion-icon aria-hidden="true" icon={searchbarCancelIcon} lazy={false}></ion-icon>
) : ( ) : (
cancelButtonText cancelButtonText
@ -727,7 +761,7 @@ export class Searchbar implements ComponentInterface {
{...this.inheritedAttributes} {...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> <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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, h, forceUpdate } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h, forceUpdate } from '@stencil/core';
import type { NotchController } from '@utils/forms'; import type { NotchController } from '@utils/forms';
@ -878,11 +879,11 @@ export class Select implements ComponentInterface {
* next to the select text. * next to the select text.
*/ */
private renderSelectIcon() { private renderSelectIcon() {
const { isExpanded, selectExpandIcon, selectCollapsedIcon } = this; const { isExpanded, selectExpandedIcon, selectCollapsedIcon } = this;
let icon = selectCollapsedIcon; let icon = selectCollapsedIcon;
if (isExpanded) { if (isExpanded) {
icon = selectExpandIcon; icon = selectExpandedIcon;
} }
return <ion-icon class="select-icon" part="icon" aria-hidden="true" icon={icon}></ion-icon>; 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. * If an icon is set on the component, use that.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 theme = getIonTheme(this);
const icon = this.expandedIcon; const defaultIcons = {
let defaultExpandIcon = theme === 'ios' ? chevronExpand : caretDownSharp; ios: chevronExpand,
ionic: caretDownRegular,
md: caretDownSharp,
};
if (icon !== undefined) { // Get the default icon based on the theme, falling back to 'md' icon if necessary
// Icon is set on the component. const defaultIcon = defaultIcons[theme] || defaultIcons.md;
return icon;
}
if (this.toggleIcon) { // Return the configured select expanded icon or the default icon
// If the toggleIcon is set, use that as the default expand icon. return config.get('selectExpandedIcon', defaultIcon);
defaultExpandIcon = this.toggleIcon;
}
return config.get('selectExpandIcon', defaultExpandIcon);
} }
/** /**
@ -967,19 +971,24 @@ export class Select implements ComponentInterface {
* If an icon is set on the component, use that. * If an icon is set on the component, use that.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 { get selectCollapsedIcon(): string {
const theme = getIonTheme(this); // Return the toggleIcon if it is explicitly set
const icon = this.toggleIcon; if (this.toggleIcon) {
const defaultIcon = theme === 'ios' ? chevronExpand : caretDownSharp; return this.toggleIcon;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
} }
// 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); 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 type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, State, Watch, h } from '@stencil/core'; import { Component, Element, Event, Host, Prop, State, Watch, h } from '@stencil/core';
import { renderHiddenInput, inheritAriaAttributes } from '@utils/helpers'; import { renderHiddenInput, inheritAriaAttributes } from '@utils/helpers';
@ -248,16 +250,25 @@ export class Toggle implements ComponentInterface {
}; };
get toggleDefaultCheckedIcon(): string { get toggleDefaultCheckedIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this); 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. * Get the icon to use for the checked icon.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 { get toggleCheckedIcon(): string {
return config.get('toggleCheckedIcon', this.toggleDefaultCheckedIcon); return config.get('toggleCheckedIcon', this.toggleDefaultCheckedIcon);
@ -267,13 +278,20 @@ export class Toggle implements ComponentInterface {
* Get the icon to use for the unchecked icon. * Get the icon to use for the unchecked icon.
* Otherwise, use the icon set in the config. * Otherwise, use the icon set in the config.
* If no icon is set in the config, use the default icon. * 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 { get toggleUncheckedIcon(): string {
// Determine the theme and map to default icons
const theme = getIonTheme(this); 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); return config.get('toggleUncheckedIcon', defaultIcon);
} }
@ -316,7 +334,8 @@ export class Toggle implements ComponentInterface {
theme === 'ios' && [this.renderOnOffSwitchLabels(true), this.renderOnOffSwitchLabels(false)]} theme === 'ios' && [this.renderOnOffSwitchLabels(true), this.renderOnOffSwitchLabels(false)]}
<div class="toggle-icon-wrapper"> <div class="toggle-icon-wrapper">
<div class="toggle-inner" part="handle"> <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> </div>
</div> </div>

View File

@ -151,14 +151,14 @@ export interface IonicConfig {
datetimePreviousIcon?: string; 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. * 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. * Overrides the expand icon for all `ion-select` components.
*/ */
selectExpandIcon?: string; selectExpandedIcon?: string;
/** /**
* Overrides the collapsed icon for all `ion-select` components. * Overrides the collapsed icon for all `ion-select` components.

View File

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