diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index 5938d3fb748..62888e05926 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -278,8 +278,8 @@ export class IonHeader { } export declare interface IonIcon extends Components.IonIcon { } -@ProxyCmp({ inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] }) -@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "", inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] }) +@ProxyCmp({ inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }) +@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "", inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }) export class IonIcon { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { diff --git a/core/src/components/progress-bar/progress-bar.scss b/core/src/components/progress-bar/progress-bar.scss index 2f70b8d6656..ae90bc52448 100644 --- a/core/src/components/progress-bar/progress-bar.scss +++ b/core/src/components/progress-bar/progress-bar.scss @@ -10,7 +10,7 @@ * @prop --progress-background: Color of the progress bar * @prop --buffer-background: Color of the buffer bar */ - --background: #{ion-color(primary, base, 0.2)}; + --background: #{ion-color(primary, base, 0.3)}; --progress-background: #{ion-color(primary, base)}; --buffer-background: var(--background); display: block; @@ -27,7 +27,7 @@ :host(.ion-color) { --progress-background: #{current-color(base)}; - --buffer-background: #{current-color(base, 0.2)}; + --buffer-background: #{current-color(base, 0.3)}; } // indeterminate has no progress-buffer-bar, so it will be added to the host @@ -72,17 +72,10 @@ } .progress-buffer-bar { - // It's currently here because --buffer-background has an alpha - // Otherwise the buffer circles would be seen through - background: #fff; - - z-index: 1; // Make it behind the progress - - &:before { - background: var(--buffer-background); + background: var(--buffer-background); - content: ""; - } + // Make it behind the progress + z-index: 1; } // MD based animation on indeterminate type @@ -109,7 +102,7 @@ top: 0; right: 0; bottom: 0; - left: -54.888891%; + left: -54.888891%; /* stylelint-enable property-blacklist */ animation: secondary-indeterminate-translate 2s infinite linear; @@ -123,8 +116,26 @@ // Buffer style // -------------------------------------------------- +.buffer-circles.buffer-circles-reversed { + /* stylelint-disable property-blacklist */ + right: unset; + left: 0; + + background-position: right; + /* stylelint-enable property-blacklist */ +} + .buffer-circles { - background: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%) repeat-x 5px center; + /* stylelint-disable property-blacklist */ + right: 0; + + left: unset; + /* stylelint-enable property-blacklist */ + + background: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%) repeat-x 5px; + + /* stylelint-disable-next-line property-blacklist */ + background-position: left; background-size: 10px 10px; z-index: 0; diff --git a/core/src/components/progress-bar/progress-bar.tsx b/core/src/components/progress-bar/progress-bar.tsx index ee952e64ab2..902ab2b621e 100644 --- a/core/src/components/progress-bar/progress-bar.tsx +++ b/core/src/components/progress-bar/progress-bar.tsx @@ -54,6 +54,7 @@ export class ProgressBar implements ComponentInterface { const { color, type, reversed, value, buffer } = this; const paused = config.getBoolean('_testing'); const mode = getIonMode(this); + const isReversed = document.dir === 'rtl' ? !reversed : reversed; return ( {type === 'indeterminate' ? renderIndeterminate() - : renderProgress(value, buffer) + : renderProgress(value, buffer, isReversed) } ); @@ -83,13 +84,13 @@ const renderIndeterminate = () => { ]; }; -const renderProgress = (value: number, buffer: number) => { +const renderProgress = (value: number, buffer: number, reversed: boolean) => { const finalValue = clamp(0, value, 1); const finalBuffer = clamp(0, buffer, 1); return [
, - finalBuffer !== 1 &&
, + finalBuffer !== 1 &&
,
, ]; }; diff --git a/core/src/components/progress-bar/test/basic/index.html b/core/src/components/progress-bar/test/basic/index.html index 688296cf98a..b428a315c6b 100644 --- a/core/src/components/progress-bar/test/basic/index.html +++ b/core/src/components/progress-bar/test/basic/index.html @@ -9,7 +9,9 @@ - @@ -619,10 +622,6 @@

Street Fighter II

- - Default - - @@ -759,6 +758,18 @@

Street Fighter II

Dark

+ +

+ + + + + + + + + +

@@ -921,22 +932,31 @@

Street Fighter II

// create component to open const element = document.createElement('div'); element.innerHTML = ` - + Super Modal - -

Content of doom

-
Here's some more content
- Dismiss Modal + + + + Super Modal + + +
+

Content of doom

+
Here's some more content
+ Dismiss Modal +
`; // present the modal const modalElement = Object.assign(document.createElement('ion-modal'), { - component: element + component: element, + swipeToClose: true, + presentingElement: document.querySelector('ion-tabs') }); // listen for close event diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index aea7e41a41c..8ee4fdd52fb 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -3,7 +3,7 @@ /* auto-generated vue proxies */ import { defineContainer } from './vue-component-lib/utils'; -import { JSX } from '@ionic/core'; +import type { JSX } from '@ionic/core'; @@ -42,10 +42,7 @@ export const IonButton = /*@__PURE__*/ defineContainer('ion-butto 'type', 'ionFocus', 'ionBlur' -], -{ - "routerLinkComponent": true -}); +]); export const IonButtons = /*@__PURE__*/ defineContainer('ion-buttons', [ @@ -64,10 +61,7 @@ export const IonCard = /*@__PURE__*/ defineContainer('ion-card', [ 'routerDirection', 'routerAnimation', 'target' -], -{ - "routerLinkComponent": true -}); +]); export const IonCardContent = /*@__PURE__*/ defineContainer('ion-card-content'); @@ -103,7 +97,8 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer('ion-c ], { "modelProp": "checked", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -186,7 +181,8 @@ export const IonDatetime = /*@__PURE__*/ defineContainer('ion-d ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -195,10 +191,7 @@ export const IonFab = /*@__PURE__*/ defineContainer('ion-fab', [ 'vertical', 'edge', 'activated' -], -{ - "routerLinkComponent": true -}); +]); export const IonFabButton = /*@__PURE__*/ defineContainer('ion-fab-button', [ @@ -218,10 +211,7 @@ export const IonFabButton = /*@__PURE__*/ defineContainer('ion 'closeIcon', 'ionFocus', 'ionBlur' -], -{ - "routerLinkComponent": true -}); +]); export const IonFabList = /*@__PURE__*/ defineContainer('ion-fab-list', [ @@ -306,7 +296,8 @@ export const IonInput = /*@__PURE__*/ defineContainer('ion-input', ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -324,10 +315,7 @@ export const IonItem = /*@__PURE__*/ defineContainer('ion-item', [ 'routerDirection', 'target', 'type' -], -{ - "routerLinkComponent": true -}); +]); export const IonItemDivider = /*@__PURE__*/ defineContainer('ion-item-divider', [ @@ -460,7 +448,8 @@ export const IonRadio = /*@__PURE__*/ defineContainer('ion-radio', ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -472,7 +461,8 @@ export const IonRadioGroup = /*@__PURE__*/ defineContainer('i ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -496,7 +486,8 @@ export const IonRange = /*@__PURE__*/ defineContainer('ion-range', ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -566,7 +557,8 @@ export const IonSearchbar = /*@__PURE__*/ defineContainer('ion ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -582,7 +574,8 @@ export const IonSegment = /*@__PURE__*/ defineContainer('ion-seg ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -594,7 +587,8 @@ export const IonSegmentButton = /*@__PURE__*/ defineContainer('ion-selec ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -710,7 +705,8 @@ export const IonTextarea = /*@__PURE__*/ defineContainer('ion-t ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -737,7 +733,8 @@ export const IonToggle = /*@__PURE__*/ defineContainer('ion-toggl ], { "modelProp": "checked", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "ionChange", + "externalModelUpdateEvent": "ionChange" }); diff --git a/packages/vue/src/vue-component-lib/utils.ts b/packages/vue/src/vue-component-lib/utils.ts index 37d388f54f8..e9a994c9a41 100644 --- a/packages/vue/src/vue-component-lib/utils.ts +++ b/packages/vue/src/vue-component-lib/utils.ts @@ -1,4 +1,4 @@ -import { VNode, defineComponent, h, inject, ref, Ref } from 'vue'; +import { VNode, defineComponent, getCurrentInstance, h, inject, ref, Ref } from 'vue'; export interface InputProps extends Object { modelValue: string | boolean; @@ -17,7 +17,7 @@ interface NavManager { interface ComponentOptions { modelProp?: string; modelUpdateEvent?: string; - routerLinkComponent?: boolean; + externalModelUpdateEvent?: string; } const getComponentClasses = (classes: unknown) => { @@ -41,7 +41,7 @@ const getElementClasses = (ref: Ref, componentClasses: * integrations. */ export const defineContainer = (name: string, componentProps: string[] = [], componentOptions: ComponentOptions = {}) => { - const { modelProp, modelUpdateEvent, routerLinkComponent } = componentOptions; + const { modelProp, modelUpdateEvent, externalModelUpdateEvent } = componentOptions; /** * Create a Vue component wrapper around a Web Component. @@ -49,29 +49,46 @@ export const defineContainer = (name: string, componentProps: string[] = * They refer to whatever properties are set on an instance of a component. */ const Container = defineComponent((props, { attrs, slots, emit }) => { + let modelPropValue = (props as any)[modelProp]; const containerRef = ref(); const classes = new Set(getComponentClasses(attrs.class)); const onVnodeBeforeMount = (vnode: VNode) => { // Add a listener to tell Vue to update the v-model if (vnode.el) { vnode.el.addEventListener(modelUpdateEvent.toLowerCase(), (e: Event) => { - emit(UPDATE_VALUE_EVENT, (e?.target as any)[modelProp]); + modelPropValue = (e?.target as any)[modelProp]; + emit(UPDATE_VALUE_EVENT, modelPropValue); + + /** + * We need to emit the change event here + * rather than on the web component to ensure + * that any v-model bindings have been updated. + * Otherwise, the developer will listen on the + * native web component, but the v-model will + * not have been updated yet. + */ + emit(externalModelUpdateEvent, e); }); } }; - let handleClick: (ev: Event) => void; - if (routerLinkComponent) { - const navManager: NavManager = inject(NAV_MANAGER); - handleClick = (ev: Event) => { - const routerProps = Object.keys(props).filter(p => p.startsWith(ROUTER_PROP_REFIX)); - if (routerProps.length === 0) return; + const currentInstance = getCurrentInstance(); + const hasRouter = currentInstance?.appContext?.provides[NAV_MANAGER]; + const navManager: NavManager | undefined = hasRouter ? inject(NAV_MANAGER) : undefined; + const handleRouterLink = (ev: Event) => { + const { routerLink } = props as any; + if (!routerLink) return; + const routerProps = Object.keys(props).filter(p => p.startsWith(ROUTER_PROP_REFIX)); + + if (navManager !== undefined) { let navigationPayload: any = { event: ev }; routerProps.forEach(prop => { navigationPayload[prop] = (props as any)[prop]; }); navManager.navigate(navigationPayload); + } else { + console.warn('Tried to navigate, but no router was found. Make sure you have mounted Vue Router.'); } } @@ -80,28 +97,28 @@ export const defineContainer = (name: string, componentProps: string[] = classes.add(value); }); + const oldClick = (props as any).onClick; + const handleClick = (ev: Event) => { + if (oldClick !== undefined) { + oldClick(ev); + } + if (!ev.defaultPrevented) { + handleRouterLink(ev); + } + } + let propsToAdd = { ...props, ref: containerRef, class: getElementClasses(containerRef, classes), - onClick: (routerLinkComponent) ? handleClick : (props as any).onClick, - onVnodeBeforeMount: (modelUpdateEvent) ? onVnodeBeforeMount : undefined + onClick: handleClick, + onVnodeBeforeMount: (modelUpdateEvent && externalModelUpdateEvent) ? onVnodeBeforeMount : undefined }; - if ((props as any).onClick) { - const oldClick = (props as any).onClick; - propsToAdd.onClick = (ev: Event) => { - oldClick(ev); - if (!ev.defaultPrevented) { - handleClick(ev); - } - }; - } - if (modelProp) { propsToAdd = { ...propsToAdd, - [modelProp]: props.hasOwnProperty('modelValue') ? props.modelValue : (props as any)[modelProp] + [modelProp]: props.hasOwnProperty('modelValue') ? props.modelValue : modelPropValue } } @@ -110,13 +127,10 @@ export const defineContainer = (name: string, componentProps: string[] = }); Container.displayName = name; - Container.props = componentProps; + Container.props = [...componentProps, ROUTER_LINK_VALUE]; if (modelProp) { Container.props.push(MODEL_VALUE); - Container.emits = [UPDATE_VALUE_EVENT]; - } - if (routerLinkComponent) { - Container.props.push(ROUTER_LINK_VALUE); + Container.emits = [UPDATE_VALUE_EVENT, externalModelUpdateEvent]; } return Container;