UNPKG

@ionic/core

Version:
782 lines (726 loc) • 22.9 kB
/** * Convert a font size to a dynamic font size. * Fonts that participate in Dynamic Type should use * dynamic font sizes. * @param size - The initial font size including the unit (i.e. px or pt) * @param unit (optional) - The unit to convert to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * a maximum font size. * @param size - The initial font size including the unit (i.e. px or pt) * @param maxScale - The maximum scale of the font (i.e. 2.5 for a maximum 250% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * a minimum font size. * @param size - The initial font size including the unit (i.e. px or pt) * @param minScale - The minimum scale of the font (i.e. 0.8 for a minimum 80% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * maximum and minimum font sizes. * @param size - The initial font size including the unit (i.e. px or pt) * @param minScale - The minimum scale of the font (i.e. 0.8 for a minimum 80% scale). * @param maxScale - The maximum scale of the font (i.e. 2.5 for a maximum 250% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * A heuristic that applies CSS to tablet * viewports. * * Usage: * @include tablet-viewport() { * :host { * background-color: green; * } * } */ /** * A heuristic that applies CSS to mobile * viewports (i.e. phones, not tablets). * * Usage: * @include mobile-viewport() { * :host { * background-color: blue; * } * } */ :host { /** * @prop --background: Background of the select * @prop --padding-top: Top padding of the select * @prop --padding-end: Right padding if direction is left-to-right, and left padding if direction is right-to-left of the select * @prop --padding-bottom: Bottom padding of the select * @prop --padding-start: Left padding if direction is left-to-right, and right padding if direction is right-to-left of the select * * @prop --placeholder-color: Color of the select placeholder text * @prop --placeholder-opacity: Opacity of the select placeholder text * * @prop --highlight-height: The height of the highlight on the select. Only applies to md mode. * @prop --highlight-color-focused: The color of the highlight on the select when focused * @prop --highlight-color-invalid: The color of the highlight on the select when invalid * @prop --highlight-color-valid: The color of the highlight on the select when valid * * @prop --border-color: Color of the select border * @prop --border-radius: Radius of the select border. A large radius may display unevenly when using fill="outline"; if needed, use shape="round" instead or increase --padding-start. * @prop --border-style: Style of the select border * @prop --border-width: Width of the select border * * @prop --ripple-color: The color of the ripple effect on MD mode. */ --padding-top: 0px; --padding-end: 0px; --padding-bottom: 0px; --padding-start: 0px; --placeholder-color: currentColor; --placeholder-opacity: var(--ion-placeholder-opacity, 0.6); --background: transparent; --border-style: solid; --highlight-color-focused: var(--ion-color-primary, #0054e9); --highlight-color-valid: var(--ion-color-success, #2dd55b); --highlight-color-invalid: var(--ion-color-danger, #c5000f); /** * This is a private API that is used to switch * out the highlight color based on the state * of the component without having to write * different selectors for different fill variants. */ --highlight-color: var(--highlight-color-focused); display: block; position: relative; width: 100%; min-height: 44px; font-family: var(--ion-font-family, inherit); white-space: nowrap; cursor: pointer; z-index: 2; } /** * Since the label sits on top of the element, * the component needs to be taller otherwise the * label will appear too close to the select text. */ :host(.select-label-placement-floating), :host(.select-label-placement-stacked) { min-height: 56px; } :host(.ion-color) { --highlight-color-focused: var(--ion-color-base); } :host(.in-item) { flex: 1 1 0; } :host(.select-disabled) { pointer-events: none; } :host(.ion-focused) button { border: 2px solid #5e9ed6; } /** * Select can be slotted * in components such as item and * toolbar which is why we do not * limit the below behavior to just ion-item. */ :host([slot=start]), :host([slot=end]) { flex: initial; width: auto; } .select-placeholder { color: var(--placeholder-color); opacity: var(--placeholder-opacity); } button { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; outline: 0; clip: rect(0 0 0 0); opacity: 0; overflow: hidden; -webkit-appearance: none; -moz-appearance: none; } .select-icon { -webkit-margin-start: 4px; margin-inline-start: 4px; -webkit-margin-end: 0; margin-inline-end: 0; margin-top: 0; margin-bottom: 0; position: relative; /** * Prevent the icon from shrinking when the label and/or * selected item text is long enough to fill the rest of * the container. */ flex-shrink: 0; } /** * Ensure that the select icon has * the correct color contrast when * used inside of an item. */ :host(.in-item-color) .select-icon { color: inherit; } /** * The select icon should be centered with * the entire container not just the control * with floating/stacked labels. */ :host(.select-label-placement-stacked) .select-icon, :host(.select-label-placement-floating) .select-icon { position: absolute; height: 100%; } /** * This positions the icon at the correct * edge of the component with LTR and RTL * text directions. The position mixin cannot be * used here because the icon is in the Shadow DOM. */ :host(.select-ltr.select-label-placement-stacked) .select-icon, :host(.select-ltr.select-label-placement-floating) .select-icon { right: var(--padding-end, 0); } :host(.select-rtl.select-label-placement-stacked) .select-icon, :host(.select-rtl.select-label-placement-floating) .select-icon { left: var(--padding-start, 0); } .select-text { flex: 1; min-width: 16px; font-size: inherit; text-overflow: ellipsis; white-space: inherit; overflow: hidden; } .select-wrapper { -webkit-padding-start: var(--padding-start); padding-inline-start: var(--padding-start); -webkit-padding-end: var(--padding-end); padding-inline-end: var(--padding-end); padding-top: var(--padding-top); padding-bottom: var(--padding-bottom); border-radius: var(--border-radius); display: flex; position: relative; flex-grow: 1; align-items: center; justify-content: space-between; height: inherit; /** * This allows developers to set the height * on the host of the element but still have * the label take up the full height * of the parent. */ min-height: inherit; transition: background-color 15ms linear; background: var(--background); line-height: normal; cursor: inherit; box-sizing: border-box; } .select-wrapper .select-placeholder { /** * When the floating label appears on top of the * select, we need to fade the text out so that the * label does not overlap with the placeholder. */ transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1); } .select-wrapper-inner { display: flex; align-items: center; overflow: hidden; } :host(.select-label-placement-stacked) .select-wrapper-inner, :host(.select-label-placement-floating) .select-wrapper-inner { /** * When using a stacked/floating label, the inner wrapper is * stacked vertically under the label container. This line * ensures that the inner wrapper fills all the remaining height * of the component. */ flex-grow: 1; } :host(.ion-touched.ion-invalid) { --highlight-color: var(--highlight-color-invalid); } /** * The component highlight is only shown * on focus, so we can safely set the valid * color state when touched/valid. If we * set it when .has-focus is present then * the highlight color would change * from the valid color to the component's * color during the transition after the * component loses focus. */ :host(.ion-valid) { --highlight-color: var(--highlight-color-valid); } .select-bottom { /** * The bottom content should take on the start and end * padding so it is always aligned with either the label * or the start of the text select. */ -webkit-padding-start: var(--padding-start); padding-inline-start: var(--padding-start); -webkit-padding-end: var(--padding-end); padding-inline-end: var(--padding-end); padding-top: 5px; padding-bottom: 0; display: flex; justify-content: space-between; border-top: var(--border-width) var(--border-style) var(--border-color); font-size: 0.75rem; white-space: normal; } /** * If the select has a validity state, the * border and label should reflect that as a color. * The invalid state should show if the select is * invalid and has already been touched. * The valid state should show if the select * is valid, has already been touched, and * is currently focused. Do not show the valid * highlight when the select is blurred. */ :host(.has-focus.ion-valid), :host(.ion-touched.ion-invalid) { --border-color: var(--highlight-color); } /** * Error text should only be shown when .ion-invalid is * present on the select. Otherwise the helper text should * be shown. */ .select-bottom .error-text { display: none; color: var(--highlight-color-invalid); } .select-bottom .helper-text { display: block; color: var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d)); } :host(.ion-touched.ion-invalid) .select-bottom .error-text { display: block; } :host(.ion-touched.ion-invalid) .select-bottom .helper-text { display: none; } .label-text-wrapper { /** * This causes the label to take up * the entire height of its container * while still keeping the text centered. */ display: flex; align-items: center; /** * Label text should not extend * beyond the bounds of the select. * However, we do not set the max * width to 100% because then * only the label would show and users * would not be able to see what they are typing. */ max-width: 200px; transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), transform 150ms cubic-bezier(0.4, 0, 0.2, 1); /** * This ensures that double tapping this text * clicks the <label> and focuses the input * when a screen reader is enabled. */ pointer-events: none; } /** * We need to use two elements instead of * one. The .label-text-wrapper is responsible * for centering the label text vertically regardless * of the input height using flexbox. * * The .label-text element is responsible for controlling * overflow when label-placement="fixed". * We want the ellipses to show up when the * fixed label overflows, but text-overflow: ellipsis only * works on block-level elements. A flex item is * considered blockified (https://www.w3.org/TR/css-display-3/#blockify). */ .label-text, ::slotted([slot=label]) { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } /** * If no label text is placed into the slot * then the element should be hidden otherwise * there will be additional margins added. */ .label-text-wrapper-hidden, .select-outline-notch-hidden { display: none; } .native-wrapper { display: flex; align-items: center; /** * When the floating label appears on top of the * input, we need to fade the input out so that the * label does not overlap with the placeholder. */ transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1); /** * This ensures that the .select-text gets * truncated with ellipses when the text is * too long for the screen. */ overflow: hidden; } :host(.select-justify-space-between) .select-wrapper { justify-content: space-between; } :host(.select-justify-start) .select-wrapper { justify-content: start; } :host(.select-justify-end) .select-wrapper { justify-content: end; } /** * Label is on the left of the input in LTR and * on the right in RTL. */ :host(.select-label-placement-start) .select-wrapper { flex-direction: row; } :host(.select-label-placement-start) .label-text-wrapper { /** * The margin between the label and * the select should be on the end * when the label sits at the start. */ -webkit-margin-start: 0; margin-inline-start: 0; -webkit-margin-end: 16px; margin-inline-end: 16px; margin-top: 0; margin-bottom: 0; } /** * Label is on the right of the select in LTR and * on the left in RTL. */ :host(.select-label-placement-end) .select-wrapper { flex-direction: row-reverse; } /** * The margin between the label and * the select should be on the start * when the label sits at the end. */ :host(.select-label-placement-end) .label-text-wrapper { -webkit-margin-start: 16px; margin-inline-start: 16px; -webkit-margin-end: 0; margin-inline-end: 0; margin-top: 0; margin-bottom: 0; } :host(.select-label-placement-fixed) .label-text-wrapper { /** * The margin between the label and * the select should be on the end * when the label sits at the start. */ -webkit-margin-start: 0; margin-inline-start: 0; -webkit-margin-end: 16px; margin-inline-end: 16px; margin-top: 0; margin-bottom: 0; } /** * Label is on the left of the select in LTR and * on the right in RTL. Label also has a fixed width. */ :host(.select-label-placement-fixed) .label-text-wrapper { flex: 0 0 100px; width: 100px; min-width: 100px; max-width: 200px; } /** * Stacked: Label sits above the select and is scaled down. * Floating: Label sits over the select when the select has no * value and is blurred. Label sits above the input and is scaled * down when the select is focused or has a value. * */ :host(.select-label-placement-stacked) .select-wrapper, :host(.select-label-placement-floating) .select-wrapper { flex-direction: column; align-items: start; } /** * Ensures that the label animates * up and to the left in LTR or * up and to the right in RTL. */ :host(.select-label-placement-stacked) .label-text-wrapper, :host(.select-label-placement-floating) .label-text-wrapper { max-width: 100%; } :host(.select-ltr.select-label-placement-stacked) .label-text-wrapper, :host(.select-ltr.select-label-placement-floating) .label-text-wrapper { transform-origin: left top; } :host(.select-rtl.select-label-placement-stacked) .label-text-wrapper, :host(.select-rtl.select-label-placement-floating) .label-text-wrapper { transform-origin: right top; } /** * Ensures the select does not * overlap the label. * Also ensure that the native wrapper * takes up the remaining available height and width. */ :host(.select-label-placement-stacked) .native-wrapper, :host(.select-label-placement-floating) .native-wrapper { margin-left: 0; margin-right: 0; margin-top: 1px; margin-bottom: 0; flex-grow: 1; width: 100%; } /** * This makes the label sit over the select * when the select is blurred and has no value. * The label should not sit over the select if the * select has a placeholder. * Note: This is different from what ion-input does * because the activating the select causes an overlay * to appear. This makes it hard to read the placeholder. * With ion-input, the input just focuses and the placeholder * is still easy to read. */ :host(.select-label-placement-floating) .label-text-wrapper { transform: translateY(100%) scale(1); } /** * The placeholder should be hidden when the label * is on top of the select. This prevents the label * from overlapping any placeholder value. * * TODO(FW-5592): Remove :not(.label-floating) piece */ :host(.select-label-placement-floating:not(.label-floating)) .native-wrapper .select-placeholder { opacity: 0; } /** * We don't use .label-floating here because that would * also include the case where the label is floating due * to content in the start/end slot. We want the opacity * to remain at the default in this case, since the select * isn't being actively interacted with. * * TODO(FW-5592): Change entire selector to: * :host(.label-floating.select-label-placement-floating) .native-wrapper .select-placeholder */ :host(.select-expanded.select-label-placement-floating) .native-wrapper .select-placeholder, :host(.ion-focused.select-label-placement-floating) .native-wrapper .select-placeholder, :host(.has-value.select-label-placement-floating) .native-wrapper .select-placeholder { opacity: 1; } /** * This makes the label sit above the input. */ :host(.label-floating) .label-text-wrapper { transform: translateY(50%) scale(0.75); /** * Label text should not extend * beyond the bounds of the input. */ max-width: calc(100% / 0.75); } ::slotted([slot=start]), ::slotted([slot=end]) { /** * Prevent the slots from shrinking when the label and/or * selected item text is long enough to fill the rest of * the container. */ flex-shrink: 0; } ::slotted([slot=start]:last-of-type) { margin-inline-end: 16px; margin-inline-start: 0; } ::slotted([slot=end]:first-of-type) { margin-inline-start: 16px; margin-inline-end: 0; } /** * Convert a font size to a dynamic font size. * Fonts that participate in Dynamic Type should use * dynamic font sizes. * @param size - The initial font size including the unit (i.e. px or pt) * @param unit (optional) - The unit to convert to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * a maximum font size. * @param size - The initial font size including the unit (i.e. px or pt) * @param maxScale - The maximum scale of the font (i.e. 2.5 for a maximum 250% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * a minimum font size. * @param size - The initial font size including the unit (i.e. px or pt) * @param minScale - The minimum scale of the font (i.e. 0.8 for a minimum 80% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * maximum and minimum font sizes. * @param size - The initial font size including the unit (i.e. px or pt) * @param minScale - The minimum scale of the font (i.e. 0.8 for a minimum 80% scale). * @param maxScale - The maximum scale of the font (i.e. 2.5 for a maximum 250% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * A heuristic that applies CSS to tablet * viewports. * * Usage: * @include tablet-viewport() { * :host { * background-color: green; * } * } */ /** * A heuristic that applies CSS to mobile * viewports (i.e. phones, not tablets). * * Usage: * @include mobile-viewport() { * :host { * background-color: blue; * } * } */ /** * Convert a font size to a dynamic font size. * Fonts that participate in Dynamic Type should use * dynamic font sizes. * @param size - The initial font size including the unit (i.e. px or pt) * @param unit (optional) - The unit to convert to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * a maximum font size. * @param size - The initial font size including the unit (i.e. px or pt) * @param maxScale - The maximum scale of the font (i.e. 2.5 for a maximum 250% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * a minimum font size. * @param size - The initial font size including the unit (i.e. px or pt) * @param minScale - The minimum scale of the font (i.e. 0.8 for a minimum 80% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * Convert a font size to a dynamic font size but impose * maximum and minimum font sizes. * @param size - The initial font size including the unit (i.e. px or pt) * @param minScale - The minimum scale of the font (i.e. 0.8 for a minimum 80% scale). * @param maxScale - The maximum scale of the font (i.e. 2.5 for a maximum 250% scale). * @param unit (optional) - The unit to convert the initial font size to. Use this if you want to * convert to a unit other than $baselineUnit. */ /** * A heuristic that applies CSS to tablet * viewports. * * Usage: * @include tablet-viewport() { * :host { * background-color: green; * } * } */ /** * A heuristic that applies CSS to mobile * viewports (i.e. phones, not tablets). * * Usage: * @include mobile-viewport() { * :host { * background-color: blue; * } * } */ :host { --border-width: 0.55px; --border-color: var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-250, var(--ion-background-color-step-250, #c8c7cc)))); --highlight-height: 0px; } .select-icon { width: 1.125rem; height: 1.125rem; color: var(--ion-color-step-650, var(--ion-text-color-step-350, #595959)); } :host(.select-label-placement-stacked) .select-wrapper-inner, :host(.select-label-placement-floating) .select-wrapper-inner { width: calc(100% - 1.125rem - 4px); } :host(.select-disabled) { opacity: 0.3; } /** * Slotted buttons have a lot of default padding that can * cause them to look misaligned from other pieces such * as the control's label, especially when using a clear * fill. We also make them circular to ensure that non- * clear buttons and the focus/hover state on clear ones * don't look too crowded. */ ::slotted(ion-button[slot=start].button-has-icon-only), ::slotted(ion-button[slot=end].button-has-icon-only) { --border-radius: 50%; --padding-start: 0; --padding-end: 0; --padding-top: 0; --padding-bottom: 0; aspect-ratio: 1; }