feat(many): expand global config for icons (#29373)

Co-authored-by: Brandy Carney <brandyscarney@users.noreply.github.com>
This commit is contained in:
Maria Hutt
2024-05-08 16:22:12 -07:00
committed by GitHub
parent 7c7c483ab9
commit a58d9fa2e1
16 changed files with 370 additions and 62 deletions

View File

@ -4,7 +4,7 @@ ion-accordion,prop,disabled,boolean,false,false,false
ion-accordion,prop,mode,"ios" | "md",undefined,false,false ion-accordion,prop,mode,"ios" | "md",undefined,false,false
ion-accordion,prop,readonly,boolean,false,false,false ion-accordion,prop,readonly,boolean,false,false,false
ion-accordion,prop,theme,"ios" | "md" | "ionic",undefined,false,false ion-accordion,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-accordion,prop,toggleIcon,string,chevronDown,false,false ion-accordion,prop,toggleIcon,string | undefined,undefined,false,false
ion-accordion,prop,toggleIconSlot,"end" | "start",'end',false,false ion-accordion,prop,toggleIconSlot,"end" | "start",'end',false,false
ion-accordion,prop,value,string,`ion-accordion-${accordionIds++}`,false,false ion-accordion,prop,value,string,`ion-accordion-${accordionIds++}`,false,false
ion-accordion,part,content ion-accordion,part,content
@ -494,7 +494,7 @@ ion-fab,method,close,close() => Promise<void>
ion-fab-button,shadow ion-fab-button,shadow
ion-fab-button,prop,activated,boolean,false,false,false ion-fab-button,prop,activated,boolean,false,false,false
ion-fab-button,prop,closeIcon,string,close,false,false ion-fab-button,prop,closeIcon,string | undefined,undefined,false,false
ion-fab-button,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true ion-fab-button,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-fab-button,prop,disabled,boolean,false,false,false ion-fab-button,prop,disabled,boolean,false,false,false
ion-fab-button,prop,download,string | undefined,undefined,false,false ion-fab-button,prop,download,string | undefined,undefined,false,false
@ -669,7 +669,7 @@ ion-item,shadow
ion-item,prop,button,boolean,false,false,false ion-item,prop,button,boolean,false,false,false
ion-item,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true ion-item,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-item,prop,detail,boolean | undefined,undefined,false,false ion-item,prop,detail,boolean | undefined,undefined,false,false
ion-item,prop,detailIcon,string,chevronForward,false,false ion-item,prop,detailIcon,string | undefined,undefined,false,false
ion-item,prop,disabled,boolean,false,false,false ion-item,prop,disabled,boolean,false,false,false
ion-item,prop,download,string | undefined,undefined,false,false ion-item,prop,download,string | undefined,undefined,false,false
ion-item,prop,href,string | undefined,undefined,false,false ion-item,prop,href,string | undefined,undefined,false,false
@ -1282,7 +1282,7 @@ ion-searchbar,prop,animated,boolean,false,false,false
ion-searchbar,prop,autocapitalize,string,'off',false,false ion-searchbar,prop,autocapitalize,string,'off',false,false
ion-searchbar,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false ion-searchbar,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
ion-searchbar,prop,cancelButtonIcon,string,config.get('backButtonIcon', arrowBackSharp) as string,false,false ion-searchbar,prop,cancelButtonIcon,string | undefined,undefined,false,false
ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false ion-searchbar,prop,cancelButtonText,string,'Cancel',false,false
ion-searchbar,prop,clearIcon,string | undefined,undefined,false,false ion-searchbar,prop,clearIcon,string | undefined,undefined,false,false
ion-searchbar,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true ion-searchbar,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true

View File

@ -96,7 +96,7 @@ export namespace Components {
/** /**
* The toggle icon to use. This icon will be rotated when the accordion is expanded or collapsed. * The toggle icon to use. This icon will be rotated when the accordion is expanded or collapsed.
*/ */
"toggleIcon": string; "toggleIcon"?: string;
/** /**
* The slot inside of `ion-item` to place the toggle icon. Defaults to `"end"`. * The slot inside of `ion-item` to place the toggle icon. Defaults to `"end"`.
*/ */
@ -1153,7 +1153,7 @@ export namespace Components {
/** /**
* The icon name to use for the close icon. This will appear when the fab button is pressed. Only applies if it is the main button inside of a fab containing a fab list. * The icon name to use for the close icon. This will appear when the fab button is pressed. Only applies if it is the main button inside of a fab containing a fab list.
*/ */
"closeIcon": string; "closeIcon"?: string;
/** /**
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). * The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/ */
@ -1530,7 +1530,7 @@ export namespace Components {
/** /**
* The icon to use when `detail` is set to `true`. * The icon to use when `detail` is set to `true`.
*/ */
"detailIcon": string; "detailIcon"?: string;
/** /**
* If `true`, the user cannot interact with the item. * If `true`, the user cannot interact with the item.
*/ */
@ -2989,7 +2989,7 @@ export namespace Components {
/** /**
* Set the cancel button icon. Only available when the theme is `"md"`. Defaults to `"arrow-back-sharp"`. * Set the cancel button icon. Only available when the theme is `"md"`. Defaults to `"arrow-back-sharp"`.
*/ */
"cancelButtonIcon": string; "cancelButtonIcon"?: string;
/** /**
* Set the the cancel button text. Only available when the theme is `"ios"`. * Set the the cancel button text. Only available when the theme is `"ios"`.
*/ */

View File

@ -79,7 +79,7 @@ export class Accordion implements ComponentInterface {
* rotated when the accordion is expanded * rotated when the accordion is expanded
* or collapsed. * or collapsed.
*/ */
@Prop() toggleIcon = chevronDown; @Prop() toggleIcon?: string;
/** /**
* The slot inside of `ion-item` to * The slot inside of `ion-item` to
@ -193,7 +193,8 @@ export class Accordion implements ComponentInterface {
return; return;
} }
const { toggleIconSlot, toggleIcon } = this; const { 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.
@ -208,7 +209,7 @@ export class Accordion implements ComponentInterface {
iconEl.slot = toggleIconSlot; iconEl.slot = toggleIconSlot;
iconEl.lazy = false; iconEl.lazy = false;
iconEl.classList.add('ion-accordion-toggle-icon'); iconEl.classList.add('ion-accordion-toggle-icon');
iconEl.icon = toggleIcon; iconEl.icon = accordionToggleIcon;
iconEl.setAttribute('aria-hidden', 'true'); iconEl.setAttribute('aria-hidden', 'true');
ionItem.appendChild(iconEl); ionItem.appendChild(iconEl);

View File

@ -82,7 +82,7 @@ export class BackButton implements ComponentInterface, ButtonInterface {
get backButtonIcon() { get backButtonIcon() {
const icon = this.icon; const icon = this.icon;
if (icon != null) { if (icon != null) {
// icon is set on the component or by the config // Icon is set on the component or by the config.
return icon; return icon;
} }

View File

@ -5,6 +5,7 @@ import { inheritAriaAttributes } from '@utils/helpers';
import { createColorClasses, hostContext, openURL } from '@utils/theme'; import { createColorClasses, hostContext, openURL } from '@utils/theme';
import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons'; import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
import type { AnimationBuilder, Color } from '../../interface'; import type { AnimationBuilder, Color } from '../../interface';
import type { RouterDirection } from '../router/utils/interface'; import type { RouterDirection } from '../router/utils/interface';
@ -167,6 +168,9 @@ 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;
@ -224,7 +228,7 @@ export class Breadcrumb implements ComponentInterface {
'breadcrumbs-collapsed-indicator': true, 'breadcrumbs-collapsed-indicator': true,
}} }}
> >
<ion-icon aria-hidden="true" icon={ellipsisHorizontal} lazy={false}></ion-icon> <ion-icon aria-hidden="true" icon={breadcrumbCollapsedIcon} lazy={false}></ion-icon>
</button> </button>
)} )}
{showSeparator && ( {showSeparator && (
@ -236,7 +240,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' ? (
<ion-icon icon={chevronForwardOutline} lazy={false} flip-rtl></ion-icon> <ion-icon icon={breadcrumbSeparatorIcon} lazy={false} flip-rtl></ion-icon>
) : ( ) : (
<span>/</span> <span>/</span>
)} )}

View File

@ -7,6 +7,7 @@ import { isRTL } from '@utils/rtl';
import { createColorClasses } from '@utils/theme'; import { createColorClasses } from '@utils/theme';
import { caretDownSharp, caretUpSharp, chevronBack, chevronDown, chevronForward } from 'ionicons/icons'; import { caretDownSharp, caretUpSharp, chevronBack, chevronDown, chevronForward } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonMode, getIonTheme } from '../../global/ionic-global'; import { getIonMode, getIonTheme } from '../../global/ionic-global';
import type { Color, StyleEventDetail, Theme } from '../../interface'; import type { Color, StyleEventDetail, Theme } from '../../interface';
@ -2115,9 +2116,10 @@ export class Datetime implements ComponentInterface {
*/ */
private renderCalendarHeader(theme: Theme) { private renderCalendarHeader(theme: Theme) {
const { disabled } = this; const { disabled, datetimeShowMonthYearIcon, datetimeHideMonthYearIcon } = this;
const expandedIcon = theme === 'ios' ? chevronDown : caretUpSharp;
const collapsedIcon = theme === 'ios' ? chevronForward : caretDownSharp; 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);
@ -2144,7 +2146,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 ? expandedIcon : collapsedIcon} icon={this.showMonthAndYear ? datetimeShowMonthYearIcon : datetimeHideMonthYearIcon}
lazy={false} lazy={false}
flipRtl={true} flipRtl={true}
></ion-icon> ></ion-icon>
@ -2160,7 +2162,7 @@ export class Datetime implements ComponentInterface {
dir={hostDir} dir={hostDir}
aria-hidden="true" aria-hidden="true"
slot="icon-only" slot="icon-only"
icon={chevronBack} icon={datetimePrevIcon}
lazy={false} lazy={false}
flipRtl flipRtl
></ion-icon> ></ion-icon>
@ -2170,7 +2172,7 @@ export class Datetime implements ComponentInterface {
dir={hostDir} dir={hostDir}
aria-hidden="true" aria-hidden="true"
slot="icon-only" slot="icon-only"
icon={chevronForward} icon={datetimeNextIcon}
lazy={false} lazy={false}
flipRtl flipRtl
></ion-icon> ></ion-icon>
@ -2593,6 +2595,34 @@ export class Datetime implements ComponentInterface {
} }
} }
/**
* 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 {
const theme = getIonTheme(this);
const expandedIcon = theme === 'ios' ? chevronDown : caretUpSharp;
return config.get('datetimeShowMonthYearIcon', expandedIcon);
}
/**
* 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 {
const theme = getIonTheme(this);
const collapsedIcon = theme === 'ios' ? chevronForward : caretDownSharp;
return config.get('datetimeHideMonthYearIcon', collapsedIcon);
}
render() { render() {
const { const {
name, name,

View File

@ -6,6 +6,7 @@ import type { Attributes } from '@utils/helpers';
import { createColorClasses, hostContext, openURL } from '@utils/theme'; import { createColorClasses, hostContext, openURL } from '@utils/theme';
import { close } from 'ionicons/icons'; import { close } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
import type { AnimationBuilder, Color } from '../../interface'; import type { AnimationBuilder, Color } from '../../interface';
import type { RouterDirection } from '../router/utils/interface'; import type { RouterDirection } from '../router/utils/interface';
@ -115,7 +116,7 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
* is pressed. Only applies if it is the main button inside of a fab containing a * is pressed. Only applies if it is the main button inside of a fab containing a
* fab list. * fab list.
*/ */
@Prop() closeIcon = close; @Prop() closeIcon?: string;
/** /**
* Emitted when the button has focus. * Emitted when the button has focus.
@ -166,6 +167,7 @@ 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
@ -196,7 +198,7 @@ export class FabButton implements ComponentInterface, AnchorInterface, ButtonInt
> >
<ion-icon <ion-icon
aria-hidden="true" aria-hidden="true"
icon={this.closeIcon} icon={fabButtonCloseIcon}
part="close-icon" part="close-icon"
class="close-icon" class="close-icon"
lazy={false} lazy={false}

View File

@ -4,6 +4,7 @@ import { printIonWarning } from '@utils/logging';
import { createColorClasses } from '@utils/theme'; import { createColorClasses } from '@utils/theme';
import { eyeOff, eye } from 'ionicons/icons'; import { eyeOff, eye } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global'; import { getIonMode } from '../../global/ionic-global';
import type { Color, TextFieldTypes } from '../../interface'; import type { Color, TextFieldTypes } from '../../interface';
@ -110,10 +111,9 @@ export class InputPasswordToggle implements ComponentInterface {
const mode = getIonMode(this); const mode = getIonMode(this);
const showPasswordIcon = this.showIcon ?? eye;
const hidePasswordIcon = this.hideIcon ?? eyeOff;
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
@ -143,7 +143,7 @@ export class InputPasswordToggle implements ComponentInterface {
<ion-icon <ion-icon
slot="icon-only" slot="icon-only"
aria-hidden="true" aria-hidden="true"
icon={isPasswordVisible ? hidePasswordIcon : showPasswordIcon} icon={isPasswordVisible ? inputPasswordHideIcon : inputPasswordShowIcon}
></ion-icon> ></ion-icon>
</ion-button> </ion-button>
</Host> </Host>

View File

@ -9,6 +9,7 @@ import type { SlotMutationController } from '@utils/slot-mutation-controller';
import { createColorClasses, hostContext } from '@utils/theme'; import { createColorClasses, hostContext } from '@utils/theme';
import { closeCircle, closeSharp } from 'ionicons/icons'; import { closeCircle, closeSharp } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
import type { AutocompleteTypes, Color, TextFieldTypes } from '../../interface'; import type { AutocompleteTypes, Color, TextFieldTypes } from '../../interface';
@ -690,16 +691,35 @@ export class Input implements ComponentInterface {
return this.renderLabel(); return this.renderLabel();
} }
/**
* Get the icon to use for the clear 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.
*
* @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 config.get('inputClearIcon', defaultClearIcon);
}
render() { render() {
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus, clearInputIcon } = this; const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus, inputClearIcon } = this;
const theme = getIonTheme(this); const theme = getIonTheme(this);
const value = this.getValue(); const value = this.getValue();
const inItem = hostContext('ion-item', this.el); const inItem = hostContext('ion-item', this.el);
const shouldRenderHighlight = theme === 'md' && fill !== 'outline' && !inItem; const shouldRenderHighlight = theme === 'md' && fill !== 'outline' && !inItem;
const defaultClearIcon = theme === 'ios' ? closeCircle : closeSharp;
const clearIconData = clearInputIcon ?? defaultClearIcon;
const hasValue = this.hasValue(); const hasValue = this.hasValue();
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null; const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
@ -806,7 +826,7 @@ export class Input implements ComponentInterface {
}} }}
onClick={this.clearTextInput} onClick={this.clearTextInput}
> >
<ion-icon aria-hidden="true" icon={clearIconData}></ion-icon> <ion-icon aria-hidden="true" icon={inputClearIcon}></ion-icon>
</button> </button>
)} )}
<slot name="end"></slot> <slot name="end"></slot>

View File

@ -6,6 +6,7 @@ import { inheritAttributes, raf } from '@utils/helpers';
import { createColorClasses, hostContext, openURL } from '@utils/theme'; import { createColorClasses, hostContext, openURL } from '@utils/theme';
import { chevronForward } from 'ionicons/icons'; import { chevronForward } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
import type { AnimationBuilder, Color, CssClassMap, StyleEventDetail } from '../../interface'; import type { AnimationBuilder, Color, CssClassMap, StyleEventDetail } from '../../interface';
import type { RouterDirection } from '../router/utils/interface'; import type { RouterDirection } from '../router/utils/interface';
@ -61,7 +62,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
/** /**
* The icon to use when `detail` is set to `true`. * The icon to use when `detail` is set to `true`.
*/ */
@Prop() detailIcon = chevronForward; @Prop() detailIcon?: string;
/** /**
* If `true`, the user cannot interact with the item. * If `true`, the user cannot interact with the item.
@ -244,7 +245,6 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
render() { render() {
const { const {
detail, detail,
detailIcon,
download, download,
labelColorStyles, labelColorStyles,
lines, lines,
@ -262,6 +262,7 @@ 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'
@ -368,12 +369,12 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
<slot name="end"></slot> <slot name="end"></slot>
{showDetail && ( {showDetail && (
<ion-icon <ion-icon
icon={detailIcon} icon={itemDetailIcon}
lazy={false} lazy={false}
class="item-detail-icon" class="item-detail-icon"
part="detail-icon" part="detail-icon"
aria-hidden="true" aria-hidden="true"
flip-rtl={detailIcon === chevronForward} flip-rtl={itemDetailIcon === chevronForward}
></ion-icon> ></ion-icon>
)} )}
</div> </div>

View File

@ -2,6 +2,7 @@ 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';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
/** /**
@ -35,9 +36,24 @@ export class Reorder implements ComponentInterface {
} }
} }
render() { /**
* 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 {
const theme = getIonTheme(this); const theme = getIonTheme(this);
const reorderIcon = theme === 'ios' ? reorderThreeOutline : reorderTwoSharp; const reorderIcon = theme === 'ios' ? reorderThreeOutline : reorderTwoSharp;
return config.get('reorderHandleIcon', reorderIcon);
}
render() {
const { reorderHandleIcon } = this;
const theme = getIonTheme(this);
return ( return (
<Host <Host
class={{ class={{
@ -45,7 +61,7 @@ export class Reorder implements ComponentInterface {
}} }}
> >
<slot> <slot>
<ion-icon icon={reorderIcon} lazy={false} class="reorder-icon" part="icon" aria-hidden="true" /> <ion-icon icon={reorderHandleIcon} lazy={false} class="reorder-icon" part="icon" aria-hidden="true" />
</slot> </slot>
</Host> </Host>
); );

View File

@ -100,7 +100,7 @@ export class Searchbar implements ComponentInterface {
* Set the cancel button icon. Only available when the theme is `"md"`. * Set the cancel button icon. Only available when the theme is `"md"`.
* Defaults to `"arrow-back-sharp"`. * Defaults to `"arrow-back-sharp"`.
*/ */
@Prop() cancelButtonIcon = config.get('backButtonIcon', arrowBackSharp) as string; @Prop() cancelButtonIcon?: string;
/** /**
* Set the the cancel button text. Only available when the theme is `"ios"`. * Set the the cancel button text. Only available when the theme is `"ios"`.
@ -609,13 +609,61 @@ export class Searchbar implements ComponentInterface {
return true; return true;
} }
/**
* Get the icon to use for the clear 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.
*
* @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 config.get('searchbarClearIcon', defaultIcon);
}
/**
* Get the icon to use for the search 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.
*
* @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 config.get('searchbarSearchIcon', defaultIcon);
}
render() { render() {
const { cancelButtonText, autocapitalize } = this; const { cancelButtonText, autocapitalize, 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 clearIcon = this.clearIcon || (theme === 'ios' ? closeCircle : closeSharp);
const searchIcon = this.searchIcon || (theme === 'ios' ? searchOutline : searchSharp);
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
@ -630,7 +678,7 @@ export class Searchbar implements ComponentInterface {
> >
<div aria-hidden="true"> <div aria-hidden="true">
{theme === 'md' ? ( {theme === 'md' ? (
<ion-icon aria-hidden="true" icon={this.cancelButtonIcon} lazy={false}></ion-icon> <ion-icon aria-hidden="true" icon={searchbarCancelIcon} lazy={false}></ion-icon>
) : ( ) : (
cancelButtonText cancelButtonText
)} )}
@ -681,7 +729,7 @@ export class Searchbar implements ComponentInterface {
{theme === 'md' && cancelButton} {theme === 'md' && cancelButton}
<ion-icon aria-hidden="true" icon={searchIcon} lazy={false} class="searchbar-search-icon"></ion-icon> <ion-icon aria-hidden="true" icon={searchbarSearchIcon} lazy={false} class="searchbar-search-icon"></ion-icon>
<button <button
aria-label="reset" aria-label="reset"
@ -698,7 +746,7 @@ export class Searchbar implements ComponentInterface {
}} }}
onClick={() => this.onClearInput(true)} onClick={() => this.onClearInput(true)}
> >
<ion-icon aria-hidden="true" icon={clearIcon} lazy={false} class="searchbar-clear-icon"></ion-icon> <ion-icon aria-hidden="true" icon={searchbarClearIcon} lazy={false} class="searchbar-clear-icon"></ion-icon>
</button> </button>
</div> </div>
{theme === 'ios' && cancelButton} {theme === 'ios' && cancelButton}

View File

@ -11,6 +11,7 @@ import { createColorClasses, hostContext } from '@utils/theme';
import { watchForOptions } from '@utils/watch-options'; import { watchForOptions } from '@utils/watch-options';
import { caretDownSharp, chevronExpand } from 'ionicons/icons'; import { caretDownSharp, chevronExpand } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
import type { import type {
ActionSheetOptions, ActionSheetOptions,
@ -863,15 +864,11 @@ export class Select implements ComponentInterface {
* next to the select text. * next to the select text.
*/ */
private renderSelectIcon() { private renderSelectIcon() {
const theme = getIonTheme(this); const { isExpanded, selectExpandIcon, selectCollapsedIcon } = this;
const { isExpanded, toggleIcon, expandedIcon } = this; let icon = selectCollapsedIcon;
let icon: string;
if (isExpanded && expandedIcon !== undefined) { if (isExpanded) {
icon = expandedIcon; icon = selectExpandIcon;
} else {
const defaultIcon = theme === 'ios' ? chevronExpand : caretDownSharp;
icon = toggleIcon ?? defaultIcon;
} }
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>;
@ -925,6 +922,53 @@ export class Select implements ComponentInterface {
); );
} }
/**
* Get the icon to use for the expand 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.
*
* @returns {string} The icon to use for the expand icon.
*/
get selectExpandIcon(): string {
const theme = getIonTheme(this);
const icon = this.expandedIcon;
let defaultExpandIcon = theme === 'ios' ? chevronExpand : caretDownSharp;
if (icon !== undefined) {
// Icon is set on the component.
return icon;
}
if (this.toggleIcon) {
// If the toggleIcon is set, use that as the default expand icon.
defaultExpandIcon = this.toggleIcon;
}
return config.get('selectExpandIcon', defaultExpandIcon);
}
/**
* Get the icon to use for the collapsed 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.
*
* @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 config.get('selectCollapsedIcon', defaultIcon);
}
render() { render() {
const { disabled, el, isExpanded, expandedIcon, labelPlacement, justify, placeholder, fill, shape, name, value } = const { disabled, el, isExpanded, expandedIcon, labelPlacement, justify, placeholder, fill, shape, name, value } =
this; this;

View File

@ -108,7 +108,9 @@
width: 15px; width: 15px;
height: 15px; height: 15px;
}
.toggle-switch-icon-checked-default {
transform: translateY(-50%) rotate(90deg); transform: translateY(-50%) rotate(90deg);
} }

View File

@ -9,7 +9,7 @@ import { checkmarkOutline, removeOutline, ellipseOutline } from 'ionicons/icons'
import { config } from '../../global/config'; import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global'; import { getIonTheme } from '../../global/ionic-global';
import type { Color, Gesture, GestureDetail, Theme } from '../../interface'; import type { Color, Gesture, GestureDetail } from '../../interface';
import type { ToggleChangeEventDetail } from './toggle-interface'; import type { ToggleChangeEventDetail } from './toggle-interface';
@ -243,21 +243,59 @@ export class Toggle implements ComponentInterface {
this.ionBlur.emit(); this.ionBlur.emit();
}; };
private getSwitchLabelIcon = (theme: Theme, checked: boolean) => { private getSwitchLabelIcon = (checked: boolean) => {
if (theme === 'md') { return checked ? this.toggleCheckedIcon : this.toggleUncheckedIcon;
return checked ? checkmarkOutline : removeOutline;
}
return checked ? removeOutline : ellipseOutline;
}; };
private renderOnOffSwitchLabels(theme: Theme, checked: boolean) { get toggleDefaultCheckedIcon(): string {
const icon = this.getSwitchLabelIcon(theme, checked); const theme = getIonTheme(this);
return theme === 'md' ? checkmarkOutline : removeOutline;
}
/**
* 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);
}
/**
* 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 {
const theme = getIonTheme(this);
const defaultIcon = theme === 'md' ? removeOutline : ellipseOutline;
return config.get('toggleUncheckedIcon', defaultIcon);
}
private renderOnOffSwitchLabels(checked: boolean) {
const icon = this.getSwitchLabelIcon(checked);
return ( return (
<ion-icon <ion-icon
class={{ class={{
'toggle-switch-icon': true, 'toggle-switch-icon': true,
'toggle-switch-icon-checked': checked, 'toggle-switch-icon-checked': checked,
/**
* The default checked icon is being modified with
* styling that causes it to rotate by 90 degrees
* when the theme is `ios`.
*
* To prevent any rotation on a custom icon that is
* set through the config, we need to apply a class
* that handles the styling only when the default
* checked icon is being used.
*/
'toggle-switch-icon-checked-default': checked && icon === this.toggleDefaultCheckedIcon,
}} }}
icon={icon} icon={icon}
aria-hidden="true" aria-hidden="true"
@ -275,10 +313,10 @@ export class Toggle implements ComponentInterface {
since the wrapper is translated when the handle is interacted with and since the wrapper is translated when the handle is interacted with and
this would move the on/off labels outside of the view box */} this would move the on/off labels outside of the view box */}
{enableOnOffLabels && {enableOnOffLabels &&
theme === 'ios' && [this.renderOnOffSwitchLabels(theme, true), this.renderOnOffSwitchLabels(theme, 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(theme, checked)} {enableOnOffLabels && theme === 'md' && this.renderOnOffSwitchLabels(checked)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -124,6 +124,108 @@ export interface IonicConfig {
*/ */
toastDuration?: number; toastDuration?: number;
/**
* Overrides the toggle icon for all `ion-accordion` components.
*/
accordionToggleIcon?: string;
/**
* Overrides the separator icon for all `ion-breadcrumb` components,
* only when mode is set to `ios`.
*/
breadcrumbSeparatorIcon?: string;
/**
* Overrides the collapsed icon for all `ion-breadcrumb` components.
*/
breadcrumbCollapsedIcon?: string;
/**
* Overrides the next icon for all `ion-datetime` components.
*/
datetimeNextIcon?: string;
/**
* Overrides the previous icon for all `ion-datetime` components.
*/
datetimePreviousIcon?: string;
/**
* Overrides the show month and year icon for all `ion-datetime` components.
*/
datetimeShowMonthYearIcon?: string;
/**
* Overrides the hide month and year icon for all `ion-datetime` components.
*/
datetimeHideMonthYearIcon?: string;
/**
* Overrides the close icon for all `ion-fab-button` components.
*/
fabButtonCloseIcon?: string;
/**
* Overrides the clear icon for all `ion-input` components.
*/
inputClearIcon?: string;
/**
* Overrides the show icon for all `ion-input-password` components.
*/
inputPasswordShowIcon?: string;
/**
* Overrides the hide icon for all `ion-input-password` components.
*/
inputPasswordHideIcon?: string;
/**
* Overrides the detail icon for all `ion-item` components.
*/
itemDetailIcon?: string;
/**
* Overrides the handle icon for all `ion-reorder` components.
*/
reorderHandleIcon?: string;
/**
* Overrides the cancel icon for all `ion-searchbar` components,
* only when mode is set to `md`.
*/
searchbarCancelIcon?: string;
/**
* Overrides the clear icon for all `ion-searchbar` components.
*/
searchbarClearIcon?: string;
/**
* Overrides the search icon for all `ion-searchbar` components.
*/
searchbarSearchIcon?: string;
/**
* Overrides the expand icon for all `ion-select` components.
*/
selectExpandIcon?: string;
/**
* Overrides the collapsed icon for all `ion-select` components.
*/
selectCollapsedIcon?: string;
/**
* Overrides the checked icon for all `ion-toggle` components.
*/
toggleCheckedIcon?: string;
/**
* Overrides the unchecked icon for all `ion-toggle` components.
*/
toggleUncheckedIcon?: string;
/** /**
* Overrides the default "animation" of all `ion-nav` and `ion-router-outlet` across the whole application. * Overrides the default "animation" of all `ion-nav` and `ion-router-outlet` across the whole application.
* This prop allows to replace the default transition and provide a custom one that applies to all navigation outlets. * This prop allows to replace the default transition and provide a custom one that applies to all navigation outlets.