Merge remote-tracking branch 'origin/main' into sp/sync-feature-7-6-with-main

This commit is contained in:
Sean Perkins
2023-11-02 13:14:57 -04:00
442 changed files with 2741 additions and 1435 deletions

View File

@ -58,19 +58,6 @@ $action-sheet-ios-title-font-weight: 400 !default;
/// @prop - Font weight of the action sheet title when it has a sub title
$action-sheet-ios-title-with-sub-title-font-weight: 600 !default;
/// @prop - Border width of the action sheet title
$action-sheet-ios-title-border-width: $hairlines-width !default;
/// @prop - Border style of the action sheet title
$action-sheet-ios-title-border-style: solid !default;
/// @prop - Border color alpha of the action sheet title
$action-sheet-ios-title-border-color-alpha: .08 !default;
/// @prop - Border color of the action sheet title
$action-sheet-ios-title-border-color: rgba($text-color-rgb, $action-sheet-ios-title-border-color-alpha) !default;
// Action Sheet Subtitle
// --------------------------------------------------
@ -129,9 +116,6 @@ $action-sheet-ios-button-background-selected: var(--ion-colo
/// @prop - Destructive text color of the action sheet button
$action-sheet-ios-button-destructive-text-color: ion-color(danger, base) !default;
/// @prop - Background color of the action sheet cancel button
$action-sheet-ios-button-cancel-background: $background-color !default;
/// @prop - Font weight of the action sheet cancel button
$action-sheet-ios-button-cancel-font-weight: 600 !default;

View File

@ -84,9 +84,6 @@ $action-sheet-md-button-padding-bottom: $action-sheet-md-button-
/// @prop - Padding start of the action sheet button
$action-sheet-md-button-padding-start: $action-sheet-md-button-padding-end !default;
/// @prop - Background color of the action sheet button
$action-sheet-md-button-background: transparent !default;
// Action Sheet Icon
// --------------------------------------------------
@ -104,6 +101,3 @@ $action-sheet-md-icon-margin-bottom: 0 !default;
/// @prop - Margin start of the icon in the action sheet button
$action-sheet-md-icon-margin-start: 0 !default;
/// @prop - Color of the icon in the action sheet button
$action-sheet-md-icon-color: $action-sheet-md-title-color !default;

View File

@ -142,8 +142,22 @@
background: var(--background);
}
.action-sheet-group::-webkit-scrollbar {
display: none;
/**
* Scrollbars on mobile devices will be hidden.
* Users can still scroll the content by swiping.
* If a user has a fine pointing device, such as a
* mouse or trackpad, then scrollbars will be
* visible. This allows users to scroll the
* content with their pointing device.
* Otherwise, the user would have to use the
* keyboard to navigate through the options.
* This may not be intuitive for users who
* are not familiar with keyboard navigation.
*/
@media (any-pointer: coarse) {
.action-sheet-group::-webkit-scrollbar {
display: none;
}
}
.action-sheet-group-cancel {

View File

@ -157,9 +157,6 @@ $alert-md-button-text-color: ion-color(primary, base) !default;
/// @prop - Background color of the alert button
$alert-md-button-background-color: transparent !default;
/// @prop - Background color of the alert activated button
$alert-md-button-background-color-activated: ion-color(primary, base, .04) !default;
/// @prop - Border radius of the alert button
$alert-md-button-border-radius: 2px !default;

View File

@ -91,10 +91,44 @@
overscroll-behavior-y: contain;
}
.alert-checkbox-group::-webkit-scrollbar,
.alert-radio-group::-webkit-scrollbar,
.alert-message::-webkit-scrollbar {
display: none;
.alert-checkbox-label,
.alert-radio-label {
/**
* This allows long words to wrap to the next line
* instead of flowing outside of the container.
*
* The "anywhere" keyword should be used instead
* of the "break-word" keyword since the parent
* container is using flexbox. Flex relies on min-content and
* max-content intrinsic sizes to structure the layout. Flex will
* wrap content only until it reaches its min-content intrinsic size
* which in this case is equal to the longest word in this container.
* "break-word" does not shrink the min-content intrinsic size
* of the flex item which causes the long word to still overflow.
* "anywhere" on the other hand does shrink the min-content
* intrinsic size which allows the long word to wrap to the next line.
*/
overflow-wrap: anywhere;
}
/**
* Scrollbars on mobile devices will be hidden.
* Users can still scroll the content by swiping.
* If a user has a fine pointing device, such as a
* mouse or trackpad, then scrollbars will be
* visible. This allows users to scroll the
* content with their pointing device.
* Otherwise, the user would have to use the
* keyboard to navigate through the options.
* This may not be intuitive for users who
* are not familiar with keyboard navigation.
*/
@media (any-pointer: coarse) {
.alert-checkbox-group::-webkit-scrollbar,
.alert-radio-group::-webkit-scrollbar,
.alert-message::-webkit-scrollbar {
display: none;
}
}
.alert-input {

View File

@ -28,6 +28,67 @@ const testAria = async (
expect(ariaDescribedBy).toBe(expectedAriaDescribedBy);
};
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('alert: text wrapping'), () => {
test('should break on words and white spaces for radios', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/28406',
});
await page.setContent(
`
<ion-alert header='Text Wrapping'></ion-alert>
<script>
const alert = document.querySelector('ion-alert');
alert.inputs = [
{ type: 'radio', value: 'a', label: 'ThisIsAllOneReallyLongWordThatShouldWrap' },
{ type: 'radio', value: 'b', label: 'These are separate words that should wrap' }
];
</script>
`,
config
);
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
const alert = page.locator('ion-alert');
await alert.evaluate((el: HTMLIonAlertElement) => el.present());
await ionAlertDidPresent.next();
await expect(page).toHaveScreenshot(screenshot(`alert-radio-text-wrap`));
});
test('should break on words and white spaces for checkboxes', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/28406',
});
await page.setContent(
`
<ion-alert header='Text Wrapping'></ion-alert>
<script>
const alert = document.querySelector('ion-alert');
alert.inputs = [
{ type: 'checkbox', value: 'a', label: 'ThisIsAllOneReallyLongWordThatShouldWrap' },
{ type: 'checkbox', value: 'b', label: 'These are separate words that should wrap' }
];
</script>
`,
config
);
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
const alert = page.locator('ion-alert');
await alert.evaluate((el: HTMLIonAlertElement) => el.present());
await ionAlertDidPresent.next();
await expect(page).toHaveScreenshot(screenshot(`alert-checkbox-text-wrap`));
});
});
});
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
test.describe(title('alert: a11y'), () => {
test.beforeEach(async ({ page }) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,5 +1,6 @@
import type { ComponentInterface } from '@stencil/core';
import { Build, Component, Element, Host, Method, h } from '@stencil/core';
import type { FocusVisibleUtility } from '@utils/focus-visible';
import { isPlatform } from '@utils/platform';
import { config } from '../../global/config';
@ -10,7 +11,7 @@ import { getIonMode } from '../../global/ionic-global';
styleUrl: 'app.scss',
})
export class App implements ComponentInterface {
private focusVisible?: any; // TODO(FW-2832): type
private focusVisible?: FocusVisibleUtility;
@Element() el!: HTMLElement;

View File

@ -41,28 +41,6 @@ $button-ios-font-size: dynamic-font-max(16px, 3)
/// @prop - Font weight of the button text
$button-ios-font-weight: 500 !default;
/// @prop - Background color of the button
$button-ios-background-color: ion-color(primary, base) !default;
/// @prop - Text color of the button
$button-ios-text-color: ion-color(primary, contrast) !default;
/// @prop - Background color of the activated button
$button-ios-background-color-activated: ion-color(primary, shade) !default;
/// @prop - Opacity of the activated button
$button-ios-opacity-activated: 1 !default;
/// @prop - Opacity of the button on hover
$button-ios-opacity-hover: .8 !default;
/// @prop - Background color of the focused button
$button-ios-background-color-focused: ion-color(primary, shade) !default;
/// @prop - Opacity of the button when disabled
$button-ios-opacity-disabled: .5 !default;
// iOS Large Button
// --------------------------------------------------
@ -129,30 +107,6 @@ $button-ios-outline-border-style: solid !default;
/// @prop - Border radius of the outline button
$button-ios-outline-border-radius: $button-ios-border-radius !default;
/// @prop - Border color of the outline button
$button-ios-outline-border-color: $button-ios-background-color !default;
/// @prop - Text color of the outline button
$button-ios-outline-text-color: $button-ios-background-color !default;
/// @prop - Background color of the outline button
$button-ios-outline-background-color: transparent !default;
/// @prop - Text color of the activated outline button
$button-ios-outline-text-color-activated: ion-color(primary, contrast) !default;
/// @prop - Background color of the activated outline button
$button-ios-outline-background-color-activated: $button-ios-background-color !default;
/// @prop - Opacity of the activated outline button
$button-ios-outline-opacity-activated: 1 !default;
/// @prop - Background color alpha of the focused outline button
$button-ios-outline-background-color-alpha-focused: .25 !default;
/// @prop - Background color of the focused outline button
$button-ios-outline-background-color-focused: ion-color(primary, base, $button-ios-outline-background-color-alpha-focused) !default;
// iOS Clear Button
// --------------------------------------------------
@ -167,30 +121,12 @@ $button-ios-clear-font-weight: normal !default;
/// @prop - Letter spacing of the button
$button-ios-letter-spacing: 0 !default;
/// @prop - Border color of the clear button
$button-ios-clear-border-color: transparent !default;
/// @prop - Background color of the clear button
$button-ios-clear-background-color: transparent !default;
/// @prop - Background color of the activated clear button
$button-ios-clear-background-color-activated: $button-ios-clear-background-color !default;
/// @prop - Opacity of the activated clear button
$button-ios-clear-opacity-activated: .4 !default;
/// @prop - Text color of the clear button on hover
$button-ios-clear-text-color-hover: $button-ios-background-color !default;
/// @prop - Opacity of the clear button on hover
$button-ios-clear-opacity-hover: .6 !default;
/// @prop - Background color alpha of the focused clear button
$button-ios-clear-background-color-alpha-focused: .25 !default;
/// @prop - Background color of the focused clear button
$button-ios-clear-background-color-focused: ion-color(primary, base, $button-ios-clear-background-color-alpha-focused) !default;
// iOS Round Button
// --------------------------------------------------

View File

@ -47,18 +47,9 @@ $button-md-letter-spacing: 0.06em;
/// @prop - Box shadow of the button
$button-md-box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12) !default;
/// @prop - Opacity of the activated button
$button-md-opacity-activated: 1 !default;
/// @prop - Box shadow of the activated button
$button-md-box-shadow-activated: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12) !default;
/// @prop - Background color of the ripple on the button
$button-md-ripple-background-color: $text-color-step-400 !default;
/// @prop - Opacity of the button when disabled
$button-md-opacity-disabled: .5 !default;
// Material Design Large Button
// --------------------------------------------------
@ -102,82 +93,6 @@ $button-md-small-min-height: 2.1em !default;
/// @prop - Font size of the small button
$button-md-small-font-size: dynamic-font(13px) !default;
// Material Design Outline Button
// --------------------------------------------------
/// @prop - Border width of the outline button
$button-md-outline-border-width: 1px !default;
/// @prop - Border style of the outline button
$button-md-outline-border-style: solid !default;
/// @prop - Background color of the outline button
$button-md-outline-background-color: transparent !default;
/// @prop - Box shadow of the outline button
$button-md-outline-box-shadow: none !default;
/// @prop - Background color alpha of the outline button on hover
$button-md-outline-background-color-alpha-hover: .1 !default;
/// @prop - Background color of the outline button on hover
$button-md-outline-background-color-hover: rgba($text-color-rgb, $button-md-outline-background-color-alpha-hover) !default;
/// @prop - Background color of the activated outline button
$button-md-outline-background-color-activated: transparent !default;
/// @prop - Box shadow of the activated outline button
$button-md-outline-box-shadow-activated: none !default;
/// @prop - Opacity of the activated outline button
$button-md-outline-opacity-activated: 1 !default;
/// @prop - Background color alpha of the focused outline button
$button-md-outline-background-color-alpha-focused: .1 !default;
/// @prop - Background color of the focused outline button
$button-md-outline-background-color-focused: ion-color(primary, base, $button-md-outline-background-color-alpha-focused) !default;
// Material Design Clear Button
// --------------------------------------------------
/// @prop - Border color of the clear button
$button-md-clear-border-color: transparent !default;
/// @prop - Background color of the clear button
$button-md-clear-background-color: transparent !default;
/// @prop - Box shadow of the clear button
$button-md-clear-box-shadow: none !default;
/// @prop - Opacity of the clear button
$button-md-clear-opacity: 1 !default;
/// @prop - Background color alpha of the activated clear button
$button-md-clear-background-color-alpha-activated: .1 !default;
/// @prop - Background color of the activated clear button
$button-md-clear-background-color-activated: rgba($text-color-rgb, $button-md-clear-background-color-alpha-activated) !default;
/// @prop - Box shadow of the activated clear button
$button-md-clear-box-shadow-activated: $button-md-clear-box-shadow !default;
/// @prop - Background color alpha of the clear button on hover
$button-md-clear-background-color-alpha-hover: .1 !default;
/// @prop - Background color of the clear button on hover
$button-md-clear-background-color-hover: rgba($text-color-rgb, $button-md-clear-background-color-alpha-hover) !default;
/// @prop - Background color of the ripple on the clear button
$button-md-clear-ripple-background-color: $text-color-step-600 !default;
/// @props - Background color of the focused clear button
$button-md-clear-background-color-alpha-focused: .1 !default;
/// @props - Background color of the focused clear button
$button-md-clear-background-color-focused: ion-color(primary, base, $button-md-clear-background-color-alpha-focused) !default;
// Material Design Round Button
// --------------------------------------------------

View File

@ -8,21 +8,12 @@
/// @prop - Background color of the checkbox when off
$checkbox-ios-background-color-off: $item-ios-background !default;
/// @prop - Background color of the checkbox when on
$checkbox-ios-background-color-on: ion-color(primary, base) !default;
/// @prop - Background color of focus indicator for checkbox when focused
$checkbox-ios-background-color-focused: ion-color(primary, tint) !default;
/// @prop - Size of the checkbox icon
$checkbox-ios-icon-size: dynamic-font-max(26px, 2.538) !default;
/// @prop - Border color of the checkbox icon when off
$checkbox-ios-icon-border-color-off: rgba($text-color-rgb, 0.23) !default;
/// @prop - Border color of the checkbox icon when on
$checkbox-ios-icon-border-color-on: $checkbox-ios-background-color-on !default;
/// @prop - Border width of the checkbox icon
$checkbox-ios-icon-border-width: dynamic-font(1px) !default;
@ -32,27 +23,6 @@ $checkbox-ios-icon-border-style: solid !default;
/// @prop - Border radius of the checkbox icon
$checkbox-ios-icon-border-radius: 50% !default;
/// @prop - Width of the checkmark border in the checkbox
$checkbox-ios-checkmark-border-width: 1px !default;
/// @prop - Style of the checkmark border in the checkbox
$checkbox-ios-checkmark-border-style: solid !default;
/// @prop - Color of the checkmark border in the checkbox
$checkbox-ios-checkmark-border-color: ion-color(primary, contrast) !default;
/// @prop - Top of the checkmark in the checkbox
$checkbox-ios-checkmark-top: calc($checkbox-ios-icon-size / 6) !default;
/// @prop - Start of the checkmark in the checkbox
$checkbox-ios-checkmark-start: calc($checkbox-ios-icon-size / 3 + 1px) !default;
/// @prop - Width of the checkmark in the checkbox
$checkbox-ios-checkmark-width: calc($checkbox-ios-icon-size / 6 + 1px) !default;
/// @prop - Height of the checkmark in the checkbox
$checkbox-ios-checkmark-height: calc($checkbox-ios-icon-size * 0.5) !default;
/// @prop - Opacity of the disabled checkbox
$checkbox-ios-disabled-opacity: $form-control-ios-disabled-opacity !default;

View File

@ -10,41 +10,20 @@ $checkbox-md-disabled-opacity: $form-control-md-disabled-opacity !defa
/// @prop - Background color of the checkbox icon when off
$checkbox-md-icon-background-color-off: $item-md-background !default;
/// @prop - Background color of focus indicator for checkbox when focused
$checkbox-md-background-color-focused: ion-color(primary, tint) !default;
/// @prop - Background color of the checkbox icon when on
$checkbox-md-icon-background-color-on: ion-color(primary, base) !default;
/// @prop - Size of the checkbox icon
/// The icon size does not use dynamic font
/// because it does not scale in native.
$checkbox-md-icon-size: 18px !default;
/// @prop - Width of the checkbox icon checkmark
$checkbox-md-icon-checkmark-width: 2px !default;
/// @prop - Style of the checkbox icon checkmark
$checkbox-md-icon-checkmark-style: solid !default;
/// @prop - Color of the checkbox icon checkmark
$checkbox-md-icon-checkmark-color: ion-color(primary, contrast) !default;
/// @prop - Border width of the checkbox icon
$checkbox-md-icon-border-width: 2px !default;
/// @prop - Border style of the checkbox icon
$checkbox-md-icon-border-style: solid !default;
/// @prop - Border radius of the checkbox icon
$checkbox-md-icon-border-radius: 2px !default;
/// @prop - Border color of the checkbox icon when off
$checkbox-md-icon-border-color-off: rgb($text-color-rgb, 0.60) !default;
/// @prop - Border color of the checkbox icon when on
$checkbox-md-icon-border-color-on: ion-color(primary, base) !default;
/// @prop - Transition duration of the checkbox
$checkbox-md-transition-duration: 180ms !default;

View File

@ -7,15 +7,6 @@ $datetime-ios-border-color: 0.55px solid $background-color-step-200 !def
/// @prop - Padding for content
$datetime-ios-padding: 16px !default;
/// @prop - Height of the time picker
$datetime-ios-time-height: 28px !default;
/// @prop - Width of the time picker
$datetime-ios-time-width: 68px !default;
/// @prop - Border radius of the time picker
$datetime-ios-time-border-radius: 8px !default;
/// @prop - The font size at which layouts may change to accommodate Dynamic Type
$datetime-dynamic-font-breakpoint: 24px !default;

View File

@ -1,4 +1,4 @@
@import "./datetime.vars";
@import "../../themes/ionic.globals";
// Datetime
// --------------------------------------------------
@ -185,13 +185,37 @@ ion-picker-column-internal {
display: none;
}
:host(.datetime-readonly),
:host(.datetime-disabled) {
pointer-events: none;
.calendar-days-of-week,
.datetime-time {
opacity: 0.4;
}
}
:host(.datetime-disabled) {
opacity: 0.4;
:host(.datetime-readonly) {
pointer-events: none;
/**
* Allow user to navigate months
* while in readonly mode
*/
.calendar-action-buttons,
.calendar-body,
.datetime-year {
pointer-events: initial;
}
/**
* Disabled buttons should have full opacity
* in readonly mode
*/
.calendar-day[disabled]:not(.calendar-day-constrained),
.datetime-action-buttons ion-button[disabled] {
opacity: 1;
}
}
/**

View File

@ -172,7 +172,7 @@ export class Datetime implements ComponentInterface {
@Prop() disabled = false;
/**
* If `true`, the datetime appears normal but is not interactive.
* If `true`, the datetime appears normal but the selected date cannot be changed.
*/
@Prop() readonly = false;
@ -599,6 +599,14 @@ export class Datetime implements ComponentInterface {
};
private setActiveParts = (parts: DatetimeParts, removeDate = false) => {
/** if the datetime component is in readonly mode,
* allow browsing of the calendar without changing
* the set value
*/
if (this.readonly) {
return;
}
const { multiple, minParts, maxParts, activeParts } = this;
/**
@ -1414,7 +1422,13 @@ export class Datetime implements ComponentInterface {
*/
private renderFooter() {
const { showDefaultButtons, showClearButton } = this;
const { disabled, readonly, showDefaultButtons, showClearButton } = this;
/**
* The cancel, clear, and confirm buttons
* should not be interactive if the datetime
* is disabled or readonly.
*/
const isButtonDisabled = disabled || readonly;
const hasSlottedButtons = this.el.querySelector('[slot="buttons"]') !== null;
if (!hasSlottedButtons && !showDefaultButtons && !showClearButton) {
return;
@ -1444,18 +1458,33 @@ export class Datetime implements ComponentInterface {
<slot name="buttons">
<ion-buttons>
{showDefaultButtons && (
<ion-button id="cancel-button" color={this.color} onClick={() => this.cancel(true)}>
<ion-button
id="cancel-button"
color={this.color}
onClick={() => this.cancel(true)}
disabled={isButtonDisabled}
>
{this.cancelText}
</ion-button>
)}
<div class="datetime-action-buttons-container">
{showClearButton && (
<ion-button id="clear-button" color={this.color} onClick={() => clearButtonClick()}>
<ion-button
id="clear-button"
color={this.color}
onClick={() => clearButtonClick()}
disabled={isButtonDisabled}
>
{this.clearText}
</ion-button>
)}
{showDefaultButtons && (
<ion-button id="confirm-button" color={this.color} onClick={() => this.confirm(true)}>
<ion-button
id="confirm-button"
color={this.color}
onClick={() => this.confirm(true)}
disabled={isButtonDisabled}
>
{this.doneText}
</ion-button>
)}
@ -1957,11 +1986,12 @@ export class Datetime implements ComponentInterface {
*/
private renderCalendarHeader(mode: Mode) {
const { disabled } = this;
const expandedIcon = mode === 'ios' ? chevronDown : caretUpSharp;
const collapsedIcon = mode === 'ios' ? chevronForward : caretDownSharp;
const prevMonthDisabled = isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
const nextMonthDisabled = isNextMonthDisabled(this.workingParts, this.maxParts);
const prevMonthDisabled = disabled || isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
const nextMonthDisabled = disabled || isNextMonthDisabled(this.workingParts, this.maxParts);
// don't use the inheritAttributes util because it removes dir from the host, and we still need that
const hostDir = this.el.getAttribute('dir') || undefined;
@ -1977,6 +2007,7 @@ export class Datetime implements ComponentInterface {
aria-label="Show year picker"
detail={false}
lines="none"
disabled={disabled}
onClick={() => {
this.toggleMonthAndYearView();
/**
@ -2043,23 +2074,28 @@ export class Datetime implements ComponentInterface {
);
}
private renderMonth(month: number, year: number) {
const { disabled, readonly } = this;
const yearAllowed = this.parsedYearValues === undefined || this.parsedYearValues.includes(year);
const monthAllowed = this.parsedMonthValues === undefined || this.parsedMonthValues.includes(month);
const isCalMonthDisabled = !yearAllowed || !monthAllowed;
const swipeDisabled = isMonthDisabled(
{
month,
year,
day: null,
},
{
// The day is not used when checking if a month is disabled.
// Users should be able to access the min or max month, even if the
// min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
minParts: { ...this.minParts, day: null },
maxParts: { ...this.maxParts, day: null },
}
);
const isDatetimeDisabled = disabled || readonly;
const swipeDisabled =
disabled ||
isMonthDisabled(
{
month,
year,
day: null,
},
{
// The day is not used when checking if a month is disabled.
// Users should be able to access the min or max month, even if the
// min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
minParts: { ...this.minParts, day: null },
maxParts: { ...this.maxParts, day: null },
}
);
// The working month should never have swipe disabled.
// Otherwise the CSS scroll snap will not work and the user
// can free-scroll the calendar.
@ -2083,7 +2119,14 @@ export class Datetime implements ComponentInterface {
const { el, highlightedDates, isDateEnabled, multiple } = this;
const referenceParts = { month, day, year };
const isCalendarPadding = day === null;
const { isActive, isToday, ariaLabel, ariaSelected, disabled, text } = getCalendarDayState(
const {
isActive,
isToday,
ariaLabel,
ariaSelected,
disabled: isDayDisabled,
text,
} = getCalendarDayState(
this.locale,
referenceParts,
this.activeParts,
@ -2094,7 +2137,8 @@ export class Datetime implements ComponentInterface {
);
const dateIsoString = convertDataToISO(referenceParts);
let isCalDayDisabled = isCalMonthDisabled || disabled;
let isCalDayDisabled = isCalMonthDisabled || isDayDisabled;
if (!isCalDayDisabled && isDateEnabled !== undefined) {
try {
@ -2113,6 +2157,15 @@ export class Datetime implements ComponentInterface {
}
}
/**
* Some days are constrained through max & min or allowed dates
* and also disabled because the component is readonly or disabled.
* These need to be displayed differently.
*/
const isCalDayConstrained = isCalDayDisabled && isDatetimeDisabled;
const isButtonDisabled = isCalDayDisabled || isDatetimeDisabled;
let dateStyle: DatetimeHighlightStyle | undefined = undefined;
/**
@ -2158,11 +2211,12 @@ export class Datetime implements ComponentInterface {
data-year={year}
data-index={index}
data-day-of-week={dayOfWeek}
disabled={isCalDayDisabled}
disabled={isButtonDisabled}
class={{
'calendar-day-padding': isCalendarPadding,
'calendar-day': true,
'calendar-day-active': isActive,
'calendar-day-constrained': isCalDayConstrained,
'calendar-day-today': isToday,
}}
part={dateParts}
@ -2237,7 +2291,7 @@ export class Datetime implements ComponentInterface {
}
private renderTimeOverlay() {
const { hourCycle, isTimePopoverOpen, locale } = this;
const { disabled, hourCycle, isTimePopoverOpen, locale } = this;
const computedHourCycle = getHourCycle(locale, hourCycle);
const activePart = this.getActivePartsWithFallback();
@ -2251,6 +2305,7 @@ export class Datetime implements ComponentInterface {
part={`time-button${isTimePopoverOpen ? ' active' : ''}`}
aria-expanded="false"
aria-haspopup="true"
disabled={disabled}
onClick={async (ev) => {
const { popoverRef } = this;

View File

@ -1,10 +0,0 @@
@import "../../themes/ionic.globals";
// Datetime
// --------------------------------------------------
/// @prop - Minimum width of the datetime
$datetime-min-width: 16px !default;
/// @prop - Minimum height of the datetime
$datetime-min-height: 1.2em !default;

View File

@ -30,3 +30,102 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
});
});
});
/**
* This behavior does not differ across
* modes/directions.
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('datetime: a11y'), () => {
test('datetime should be keyboard navigable', async ({ page, browserName }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00"></ion-datetime>
`,
config
);
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
const datetime = page.locator('ion-datetime');
const monthYearButton = page.locator('.calendar-month-year ion-item');
const prevButton = page.locator('.calendar-next-prev ion-button:nth-child(1)');
const nextButton = page.locator('.calendar-next-prev ion-button:nth-child(2)');
await page.keyboard.press(tabKey);
await expect(monthYearButton).toBeFocused();
await page.keyboard.press(tabKey);
await expect(prevButton).toBeFocused();
await page.keyboard.press(tabKey);
await expect(nextButton).toBeFocused();
// check value before & after selecting via keyboard
const initialValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
expect(initialValue).toBe('2022-02-22T16:30:00');
await page.keyboard.press(tabKey);
await page.waitForChanges();
await page.keyboard.press('ArrowLeft');
await page.waitForChanges();
await page.keyboard.press('Enter');
await page.waitForChanges();
const newValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
expect(newValue).not.toBe('2022-02-22T16:30:00');
});
test('buttons should be keyboard navigable', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" show-default-buttons="true" show-clear-button="true"></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const clearButton = page.locator('#clear-button button');
const selectedDay = page.locator('.calendar-day-active');
await expect(selectedDay).toHaveText('22');
await clearButton.focus();
await page.waitForChanges();
await expect(clearButton).toBeFocused();
await page.keyboard.press('Enter');
await page.waitForChanges();
await expect(selectedDay).toHaveCount(0);
});
test('should navigate through months via right arrow key', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-28"></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
const calendarBody = page.locator('.calendar-body');
await expect(calendarMonthYear).toHaveText('February 2022');
await calendarBody.focus();
await page.waitForChanges();
await page.keyboard.press('ArrowRight');
await page.waitForChanges();
await expect(calendarMonthYear).toHaveText('March 2022');
});
});
});

View File

@ -0,0 +1,103 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not differ across
* modes/directions.
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config, screenshot }) => {
test.describe(title('datetime: disabled'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-05T00:00:00" min="2022-01-01T00:00:00" max="2022-02-20T23:59:59" day-values="5,6,10,11,15,16,20" show-default-buttons disabled></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
await expect(datetime).toHaveScreenshot(screenshot(`datetime-disabled`));
});
test('date should be disabled', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-28" disabled></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const febFirstButton = page.locator(`.calendar-day[data-day='1'][data-month='2']`);
await expect(febFirstButton).toBeDisabled();
});
test('month-year button should be disabled', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-28" disabled></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
await expect(calendarMonthYear.locator('button')).toBeDisabled();
});
test('next and prev buttons should be disabled', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-28" disabled></ion-datetime>
`,
config
);
const prevMonthButton = page.locator('ion-datetime .calendar-next-prev ion-button:first-of-type button');
const nextMonthButton = page.locator('ion-datetime .calendar-next-prev ion-button:last-of-type button');
await expect(prevMonthButton).toBeDisabled();
await expect(nextMonthButton).toBeDisabled();
});
test('clear button should be disabled', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" show-default-buttons="true" show-clear-button="true" disabled></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const clearButton = page.locator('#clear-button button');
await expect(clearButton).toBeDisabled();
});
test('should not navigate through months via right arrow key', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-28" disabled></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
const calendarBody = page.locator('.calendar-body');
await expect(calendarMonthYear).toHaveText('February 2022');
await calendarBody.focus();
await page.waitForChanges();
await page.keyboard.press('ArrowRight');
await page.waitForChanges();
await expect(calendarMonthYear).toHaveText('February 2022');
});
});
});

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Datetime - Disabled</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(250px, 1fr));
grid-row-gap: 20px;
grid-column-gap: 20px;
}
h2 {
font-size: 12px;
font-weight: normal;
color: #6f7378;
margin-top: 10px;
margin-left: 5px;
}
@media screen and (max-width: 800px) {
.grid {
grid-template-columns: 1fr;
padding: 0;
}
}
ion-datetime {
width: 350px;
}
</style>
</head>
<body>
<ion-app>
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Datetime - Disabled</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="grid">
<div class="grid-item">
<h2>Inline - Default Value</h2>
<ion-datetime id="inline-datetime-value" disabled value="2022-04-21T00:00:00"></ion-datetime>
</div>
<div class="grid-item">
<h2>Inline</h2>
<ion-datetime
id="inline-datetime"
presentation="date"
disabled
show-default-buttons="true"
show-clear-button="true"
multiple="true"
></ion-datetime>
</div>
<div class="grid-item">
<h2>Inline - No Default Value</h2>
<ion-datetime id="inline-datetime-no-value" disabled></ion-datetime>
</div>
</div>
</ion-content>
<script>
const firstDatetime = document.querySelector('#inline-datetime');
firstDatetime.value = ['2023-08-03', '2023-08-13', '2023-08-29'];
</script>
</ion-app>
</body>
</html>

View File

@ -0,0 +1,167 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not differ across
* modes/directions.
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config, screenshot }) => {
test.describe(title('datetime: readonly'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-05T00:00:00" min="2022-01-01T00:00:00" max="2022-02-20T23:59:59" day-values="5,6,10,11,15,16,20" show-default-buttons readonly></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
await expect(datetime).toHaveScreenshot(screenshot(`datetime-readonly`));
});
test('date should be disabled', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-28" readonly></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const febFirstButton = page.locator(`.calendar-day[data-day='1'][data-month='2']`);
await expect(febFirstButton).toBeDisabled();
});
test('should navigate months via month-year button', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
`,
config
);
const ionChange = await page.spyOnEvent('ionChange');
await page.waitForSelector('.datetime-ready');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
await expect(calendarMonthYear).toHaveText('February 2022');
await calendarMonthYear.click();
await page.waitForChanges();
await page.locator('.month-column .picker-item[data-value="3"]').click();
await page.waitForChanges();
await expect(calendarMonthYear).toHaveText('March 2022');
await expect(ionChange).not.toHaveReceivedEvent();
});
test('should open picker using keyboard navigation', async ({ page, browserName }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
`,
config
);
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
await page.waitForSelector('.datetime-ready');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
const monthYearButton = page.locator('.calendar-month-year ion-item');
await expect(calendarMonthYear).toHaveText('February 2022');
await page.keyboard.press(tabKey);
await expect(monthYearButton).toBeFocused();
await page.waitForChanges();
await page.keyboard.press('Enter');
await page.waitForChanges();
const marchPickerItem = page.locator('.month-column .picker-item[data-value="3"]');
await expect(marchPickerItem).toBeVisible();
});
test('should view next month via next button', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
`,
config
);
const ionChange = await page.spyOnEvent('ionChange');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
await expect(calendarMonthYear).toHaveText('February 2022');
const nextMonthButton = page.locator('ion-datetime .calendar-next-prev ion-button + ion-button');
await nextMonthButton.click();
await page.waitForChanges();
await expect(calendarMonthYear).toHaveText('March 2022');
await expect(ionChange).not.toHaveReceivedEvent();
});
test('should not change value when the month is changed via keyboard navigation', async ({ page, browserName }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" readonly></ion-datetime>
`,
config
);
const tabKey = browserName === 'webkit' ? 'Alt+Tab' : 'Tab';
const datetime = page.locator('ion-datetime');
const monthYearButton = page.locator('.calendar-month-year ion-item');
const prevButton = page.locator('.calendar-next-prev ion-button:nth-child(1)');
const nextButton = page.locator('.calendar-next-prev ion-button:nth-child(2)');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
await page.keyboard.press(tabKey);
await expect(monthYearButton).toBeFocused();
await page.keyboard.press(tabKey);
await expect(prevButton).toBeFocused();
await page.keyboard.press(tabKey);
await expect(nextButton).toBeFocused();
// check value before & after selecting via keyboard
const initialValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
expect(initialValue).toBe('2022-02-22T16:30:00');
await expect(calendarMonthYear).toHaveText('February 2022');
await page.keyboard.press(tabKey);
await page.waitForChanges();
await page.keyboard.press('ArrowLeft');
await page.waitForChanges();
await expect(calendarMonthYear).toHaveText('January 2022');
await page.keyboard.press('Enter');
await page.waitForChanges();
const newValue = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
// should not have changed
expect(newValue).toBe('2022-02-22T16:30:00');
});
test('clear button should be disabled', async ({ page }) => {
await page.setContent(
`
<ion-datetime value="2022-02-22T16:30:00" show-default-buttons="true" show-clear-button="true" readonly></ion-datetime>
`,
config
);
await page.waitForSelector('.datetime-ready');
const clearButton = page.locator('#clear-button button');
await expect(clearButton).toBeDisabled();
});
});
});

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Datetime - Readonly</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(250px, 1fr));
grid-row-gap: 20px;
grid-column-gap: 20px;
}
h2 {
font-size: 12px;
font-weight: normal;
color: #6f7378;
margin-top: 10px;
margin-left: 5px;
}
@media screen and (max-width: 800px) {
.grid {
grid-template-columns: 1fr;
padding: 0;
}
}
ion-datetime {
width: 350px;
}
</style>
</head>
<body>
<ion-app>
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Datetime - Readonly</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="grid">
<div class="grid-item">
<h2>Inline</h2>
<ion-datetime
id="inline-datetime"
presentation="date"
readonly
show-default-buttons="true"
show-clear-button="true"
multiple="true"
></ion-datetime>
</div>
<div class="grid-item">
<h2>Inline - No Default Value</h2>
<ion-datetime id="inline-datetime-no-value" readonly></ion-datetime>
</div>
</div>
</ion-content>
<script>
const firstDatetime = document.querySelector('#inline-datetime');
firstDatetime.value = ['2023-08-03', '2023-08-13', '2023-08-29'];
firstDatetime.isDateEnabled = (dateString) => {
const date = new Date(dateString);
const utcDay = date.getUTCDay();
/**
* Date will be enabled if it is not
* Sunday or Saturday
*/
return utcDay !== 0 && utcDay !== 6;
};
</script>
</ion-app>
</body>
</html>

View File

@ -12,9 +12,6 @@ $fab-md-box-shadow-activated: 0 7px 8px -4px rgba(0, 0, 0, .
/// @prop - Background color of the button
$fab-md-background-color: ion-color(primary, base) !default;
/// @prop - Background color of the activated button
$fab-md-background-color-activated: ion-color(primary, contrast) !default;
/// @prop - Text color of the button
$fab-md-text-color: ion-color(primary, contrast) !default;

View File

@ -23,15 +23,29 @@
}
:host(.fab-horizontal-start) {
@include position-horizontal(
calc(#{$fab-content-margin} + var(--ion-safe-area-left, 0px)), null
);
/* stylelint-disable */
@include ltr() {
left: calc(#{$fab-content-margin} + var(--ion-safe-area-left, 0px));
}
@include rtl() {
right: calc(#{$fab-content-margin} + var(--ion-safe-area-right, 0px));
left: unset;
}
/* stylelint-enable */
}
:host(.fab-horizontal-end) {
@include position-horizontal(
null, calc(#{$fab-content-margin} + var(--ion-safe-area-right, 0px))
);
/* stylelint-disable */
@include ltr() {
right: calc(#{$fab-content-margin} + var(--ion-safe-area-right, 0px));
}
@include rtl() {
left: calc(#{$fab-content-margin} + var(--ion-safe-area-left, 0px));
right: unset;
}
/* stylelint-enable */
}

View File

@ -0,0 +1,72 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs({ modes: ['md'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('fab: safe area'), () => {
test('should ignore document direction in safe area positioning for start-positioned fab', async ({ page }) => {
await page.setContent(
`
<style>
:root {
--ion-safe-area-left: 40px;
--ion-safe-area-right: 20px;
}
</style>
<ion-content>
<ion-fab vertical="center" horizontal="start">
<ion-fab-button>
<ion-icon name="add"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
`,
config
);
/**
* We need to capture the entire page to check the fab's position,
* but we don't need much extra white space.
*/
await page.setViewportSize({
width: 200,
height: 200,
});
await expect(page).toHaveScreenshot(screenshot('fab-safe-area-horizontal-start'));
});
test('should ignore document direction in safe area positioning for end-positioned fab', async ({ page }) => {
await page.setContent(
`
<style>
:root {
--ion-safe-area-left: 40px;
--ion-safe-area-right: 20px;
}
</style>
<ion-content>
<ion-fab vertical="center" horizontal="end">
<ion-fab-button>
<ion-icon name="add"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
`,
config
);
/**
* We need to capture the entire page to check the fab's position,
* but we don't need much extra white space.
*/
await page.setViewportSize({
width: 200,
height: 200,
});
await expect(page).toHaveScreenshot(screenshot('fab-safe-area-horizontal-end'));
});
});
});

View File

@ -28,24 +28,6 @@
height: 18px;
}
// Input Wrapper
// ----------------------------------------------------------------
:host(:not(.legacy-input)) {
min-height: 44px;
}
/**
* Since the label sits on top of the element,
* the component needs to be taller otherwise the
* label will appear too close to the input text.
*/
:host(.input-label-placement-floating),
:host(.input-label-placement-stacked) {
min-height: 56px;
}
// Input - Disabled
// ----------------------------------------------------------------
// The input, label, helper text, char counter and placeholder

View File

@ -19,59 +19,5 @@ $input-ios-padding-bottom: $item-ios-padding-bottom !default;
/// @prop - Margin start of the input
$input-ios-padding-start: 0 !default;
/// @prop - Margin start of the input when it is after a label
$input-ios-by-label-margin-start: $item-ios-padding-start !default;
/// @prop - Padding top of the inset input
$input-ios-inset-padding-top: ($item-ios-padding-top * 0.5) !default;
/// @prop - Padding end of the inset input
$input-ios-inset-padding-end: ($item-ios-padding-end * 0.5) !default;
/// @prop - Padding bottom of the inset input
$input-ios-inset-padding-bottom: ($item-ios-padding-bottom * 0.5) !default;
/// @prop - Padding start of the inset input
$input-ios-inset-padding-start: ($item-ios-padding-start * 0.5) !default;
/// @prop - Margin top of the inset input
$input-ios-inset-margin-top: ($item-ios-padding-top * 0.5) !default;
/// @prop - Margin end of the inset input
$input-ios-inset-margin-end: $item-ios-padding-end !default;
/// @prop - Margin bottom of the inset input
$input-ios-inset-margin-bottom: ($item-ios-padding-bottom * 0.5) !default;
/// @prop - Margin start of the inset input
$input-ios-inset-margin-start: 0 !default;
/// @prop - Width of the icon used to clear the input
$input-ios-input-clear-icon-width: 30px !default;
/// @prop - Padding end of the input with clear input
$input-ios-input-clear-padding-end: ($input-ios-input-clear-icon-width + $item-ios-padding-end) !default;
/// @prop - Placeholder Text color of the input
$input-ios-placeholder-color: $placeholder-text-color !default;
/// @prop - Show the focus highlight when the input has focus
$input-ios-show-focus-highlight: false !default;
/// @prop - Show the valid highlight when it is valid and has a value
$input-ios-show-valid-highlight: $input-ios-show-focus-highlight !default;
/// @prop - Show the invalid highlight when it is invalid and has value
$input-ios-show-invalid-highlight: $input-ios-show-focus-highlight !default;
/// @prop - Color of the input highlight
$input-ios-highlight-color: ion-color(primary, base) !default;
/// @prop - Color of the input highlight when valid
$input-ios-highlight-color-valid: ion-color(success, base) !default;
/// @prop - Color of the input highlight when invalid
$input-ios-highlight-color-invalid: ion-color(danger, base) !default;
/// @prop - The opacity of the input text, label, helper text, char counter and placeholder of a disabled input
$input-ios-disabled-opacity: $form-control-ios-disabled-opacity !default;

View File

@ -8,6 +8,8 @@
--border-radius: 4px;
--padding-start: 16px;
--padding-end: 16px;
min-height: 56px;
}
:host(.input-fill-outline.input-shape-round) {

View File

@ -60,13 +60,6 @@
letter-spacing: .0333333333em;
}
// Input Wrapper
// ----------------------------------------------------------------
:host(:not(.legacy-input)) {
min-height: 56px;
}
// Input Label
// ----------------------------------------------------------------

View File

@ -9,6 +9,8 @@
--border-radius: 4px;
--padding-start: 16px;
--padding-end: 16px;
min-height: 56px;
}
/**

View File

@ -19,54 +19,6 @@ $input-md-padding-bottom: 10px !default;
/// @prop - Margin start of the input
$input-md-padding-start: ($item-md-padding-start * 0.5) !default;
/// @prop - Width of the icon used to clear the input
$input-md-input-clear-icon-width: 30px !default;
/// @prop - Placeholder Text color of the input
$input-md-placeholder-color: $placeholder-text-color !default;
/// @prop - Show the focus highlight when the input has focus
$input-md-show-focus-highlight: true !default;
/// @prop - Show the valid highlight when it is valid and has a value
$input-md-show-valid-highlight: $input-md-show-focus-highlight !default;
/// @prop - Show the invalid highlight when it is invalid and has value
$input-md-show-invalid-highlight: $input-md-show-focus-highlight !default;
/// @prop - Color of the input highlight
$input-md-highlight-color: ion-color(primary, base) !default;
/// @prop - Color of the input highlight when valid
$input-md-highlight-color-valid: ion-color(success, base) !default;
/// @prop - Color of the input highlight when invalid
$input-md-highlight-color-invalid: ion-color(danger, base) !default;
/// @prop - Padding top of the inset input
$input-md-inset-padding-top: ($item-md-padding-top * 0.5) !default;
/// @prop - Padding end of the inset input
$input-md-inset-padding-end: 0 !default;
/// @prop - Padding bottom of the inset input
$input-md-inset-padding-bottom: ($item-md-padding-bottom * 0.5) !default;
/// @prop - Padding start of the inset input
$input-md-inset-padding-start: ($item-md-padding-start * 0.5) !default;
/// @prop - Margin top of the inset input
$input-md-inset-margin-top: ($item-md-padding-top * 0.5) !default;
/// @prop - Margin end of the inset input
$input-md-inset-margin-end: $item-md-padding-end !default;
/// @prop - Margin bottom of the inset input
$input-md-inset-margin-bottom: ($item-md-padding-bottom * 0.5) !default;
/// @prop - Margin start of the inset input
$input-md-inset-margin-start: $item-md-padding-start !default;
/// @prop - The amount of whitespace to display on either side of the floating label
$input-md-floating-label-padding: 4px !default;

View File

@ -101,6 +101,23 @@
--highlight-color-focused: #{current-color(base)};
}
// Input Wrapper
// ----------------------------------------------------------------
:host(:not(.legacy-input)) {
min-height: 44px;
}
/**
* Since the label sits on top of the element,
* the component needs to be taller otherwise the
* label will appear too close to the input text.
*/
:host(.input-label-placement-floating),
:host(.input-label-placement-stacked) {
min-height: 56px;
}
// Native Text Input
// --------------------------------------------------

View File

@ -3,7 +3,13 @@ import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, for
import type { LegacyFormController, NotchController } from '@utils/forms';
import { createLegacyFormController, createNotchController } from '@utils/forms';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes } from '@utils/helpers';
import {
inheritAriaAttributes,
debounceEvent,
findItemLabel,
inheritAttributes,
componentOnReady,
} from '@utils/helpers';
import { printIonWarning } from '@utils/logging';
import { createSlotMutationController } from '@utils/slot-mutation-controller';
import type { SlotMutationController } from '@utils/slot-mutation-controller';
@ -430,7 +436,14 @@ export class Input implements ComponentInterface {
* Returns the native `<input>` element used under the hood.
*/
@Method()
getInputElement(): Promise<HTMLInputElement> {
async getInputElement(): Promise<HTMLInputElement> {
/**
* If this gets called in certain early lifecycle hooks (ex: Vue onMounted),
* nativeInput won't be defined yet with the custom elements build, so wait for it to load in.
*/
if (!this.nativeInput) {
await new Promise((resolve) => componentOnReady(this.el, resolve));
}
return Promise.resolve(this.nativeInput!);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 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: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 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.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 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.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.1 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.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 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.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 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.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 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: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.1 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: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.1 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: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Some files were not shown because too many files have changed in this diff Show More