Compare commits

...

3 Commits

Author SHA1 Message Date
BenOsodrac
84e281a5e3 removed host-context 2025-02-18 20:17:11 +00:00
BenOsodrac
98d1f52b35 add ion-status-hint poc 2025-02-18 19:41:37 +00:00
BenOsodrac
53c6d18e72 approach 2 poc 2025-02-18 13:56:00 +00:00
13 changed files with 396 additions and 9 deletions

View File

@@ -313,10 +313,12 @@ ion-backdrop,event,ionBackdropTap,void,true
ion-badge,shadow
ion-badge,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-badge,prop,hintPosition,"bottom-right" | "top-right",'top-right',false,false
ion-badge,prop,mode,"ios" | "md",undefined,false,false
ion-badge,prop,shape,"round | rectangular" | "soft" | undefined,undefined,false,false
ion-badge,prop,size,"large" | "medium" | "small" | "xlarge" | "xsmall" | "xxsmall" | undefined,undefined,false,false
ion-badge,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-badge,prop,useAsHint,boolean,false,false,false
ion-badge,css-prop,--background,ionic
ion-badge,css-prop,--background,ios
ion-badge,css-prop,--background,md
@@ -2181,6 +2183,14 @@ ion-split-pane,css-prop,--side-width,ionic
ion-split-pane,css-prop,--side-width,ios
ion-split-pane,css-prop,--side-width,md
ion-status-hint,shadow
ion-status-hint,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-status-hint,prop,mode,"ios" | "md",undefined,false,false
ion-status-hint,prop,position,"bottom-right" | "static" | "top-right",'static',false,false
ion-status-hint,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
ion-status-hint,prop,size,"large" | "medium" | "small" | undefined,undefined,false,false
ion-status-hint,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-tab,shadow
ion-tab,prop,component,Function | HTMLElement | null | string | undefined,undefined,false,false
ion-tab,prop,mode,"ios" | "md",undefined,false,false

View File

@@ -424,6 +424,7 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
"hintPosition": 'top-right' | 'bottom-right';
/**
* The mode determines the platform behaviors of the component.
*/
@@ -440,6 +441,7 @@ export namespace Components {
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
"useAsHint": boolean;
}
interface IonBreadcrumb {
/**
@@ -3425,6 +3427,26 @@ export namespace Components {
*/
"when": string | boolean;
}
interface IonStatusHint {
"color"?: Color;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
"position": 'top-right' | 'bottom-right' | 'static';
/**
* Set to `"soft"` for an hint with slightly rounded corners, `"round"` for an hint with fully rounded corners, or `"rectangular"` for an hint without rounded corners. Defaults to `"round"`.
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Set to `"small"` for a compact size. Set to `"medium"` for the default height and width. Set to `"large"` for a larger size. Defaults to `"small"`.
*/
"size"?: 'small' | 'medium' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonTab {
"active": boolean;
/**
@@ -5151,6 +5173,12 @@ declare global {
prototype: HTMLIonSplitPaneElement;
new (): HTMLIonSplitPaneElement;
};
interface HTMLIonStatusHintElement extends Components.IonStatusHint, HTMLStencilElement {
}
var HTMLIonStatusHintElement: {
prototype: HTMLIonStatusHintElement;
new (): HTMLIonStatusHintElement;
};
interface HTMLIonTabElement extends Components.IonTab, HTMLStencilElement {
}
var HTMLIonTabElement: {
@@ -5395,6 +5423,7 @@ declare global {
"ion-skeleton-text": HTMLIonSkeletonTextElement;
"ion-spinner": HTMLIonSpinnerElement;
"ion-split-pane": HTMLIonSplitPaneElement;
"ion-status-hint": HTMLIonStatusHintElement;
"ion-tab": HTMLIonTabElement;
"ion-tab-bar": HTMLIonTabBarElement;
"ion-tab-button": HTMLIonTabButtonElement;
@@ -5784,6 +5813,7 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
"hintPosition"?: 'top-right' | 'bottom-right';
/**
* The mode determines the platform behaviors of the component.
*/
@@ -5800,6 +5830,7 @@ declare namespace LocalJSX {
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
"useAsHint"?: boolean;
}
interface IonBreadcrumb {
/**
@@ -8868,6 +8899,26 @@ declare namespace LocalJSX {
*/
"when"?: string | boolean;
}
interface IonStatusHint {
"color"?: Color;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
"position"?: 'top-right' | 'bottom-right' | 'static';
/**
* Set to `"soft"` for an hint with slightly rounded corners, `"round"` for an hint with fully rounded corners, or `"rectangular"` for an hint without rounded corners. Defaults to `"round"`.
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Set to `"small"` for a compact size. Set to `"medium"` for the default height and width. Set to `"large"` for a larger size. Defaults to `"small"`.
*/
"size"?: 'small' | 'medium' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonTab {
"active"?: boolean;
/**
@@ -9464,6 +9515,7 @@ declare namespace LocalJSX {
"ion-skeleton-text": IonSkeletonText;
"ion-spinner": IonSpinner;
"ion-split-pane": IonSplitPane;
"ion-status-hint": IonStatusHint;
"ion-tab": IonTab;
"ion-tab-bar": IonTabBar;
"ion-tab-button": IonTabButton;
@@ -9566,6 +9618,7 @@ declare module "@stencil/core" {
"ion-skeleton-text": LocalJSX.IonSkeletonText & JSXBase.HTMLAttributes<HTMLIonSkeletonTextElement>;
"ion-spinner": LocalJSX.IonSpinner & JSXBase.HTMLAttributes<HTMLIonSpinnerElement>;
"ion-split-pane": LocalJSX.IonSplitPane & JSXBase.HTMLAttributes<HTMLIonSplitPaneElement>;
"ion-status-hint": LocalJSX.IonStatusHint & JSXBase.HTMLAttributes<HTMLIonStatusHintElement>;
"ion-tab": LocalJSX.IonTab & JSXBase.HTMLAttributes<HTMLIonTabElement>;
"ion-tab-bar": LocalJSX.IonTabBar & JSXBase.HTMLAttributes<HTMLIonTabBarElement>;
"ion-tab-button": LocalJSX.IonTabButton & JSXBase.HTMLAttributes<HTMLIonTabButtonElement>;

View File

@@ -48,6 +48,13 @@
color: #{color.current-color(contrast)};
}
:host(:empty) {
:host(:empty):not(.badge-hint) {
display: none;
}
ion-avatar ::slotted(ion-badge) {
position: absolute;
right: 0;
font-size: var(--ion-font-size-275, 0.6875rem);
}

View File

@@ -47,6 +47,10 @@ export class Badge implements ComponentInterface {
*/
@Prop() size?: 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
@Prop() useAsHint: boolean = false;
@Prop() hintPosition: 'top-right' | 'bottom-right' = 'top-right';
private getShape(): string | undefined {
const theme = getIonTheme(this);
const { shape } = this;
@@ -63,11 +67,12 @@ export class Badge implements ComponentInterface {
return shape;
}
private getSize(): string | undefined {
const theme = getIonTheme(this);
const { size } = this;
const { size, useAsHint } = this;
// TODO(ROU-10747): Remove theme check when sizes are defined for all themes.
// TODO: Remove theme check when sizes are defined for all themes.
if (theme !== 'ionic') {
return undefined;
}
@@ -76,9 +81,14 @@ export class Badge implements ComponentInterface {
return 'small';
}
if (useAsHint && size && !['small', 'medium', 'large'].includes(size)) {
return 'small';
}
return size;
}
render() {
const shape = this.getShape();
const size = this.getSize();
@@ -89,6 +99,8 @@ export class Badge implements ComponentInterface {
[theme]: true,
[`badge-${shape}`]: shape !== undefined,
[`badge-${size}`]: size !== undefined,
['badge-hint']: this.useAsHint,
[`badge-hint-${this.hintPosition}`]: this.useAsHint,
})}
>
<slot></slot>

View File

@@ -304,4 +304,4 @@ ion-ripple-effect {
:host(.button-solid.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
background: #{var(--ion-toolbar-color, var(--background))};
color: #{var(--ion-toolbar-background, var(--color))};
}
}

View File

@@ -0,0 +1,91 @@
@use "../../themes/ionic/ionic.globals.scss" as globals;
@use "../../themes/functions.color" as color;
// Ionic Badge
// --------------------------------------------------
:host {
--background: #{globals.ion-color(primary, base)};
--color: #{globals.ion-color(primary, contrast)};
display: inline-flex;
position: static;
align-items: center;
justify-content: center;
font-size: globals.$ion-font-size-275;
overflow: hidden;
}
:host(.ion-color) {
background: #{color.current-color(base)};
color: #{color.current-color(contrast)};
}
// Hint Positions
// --------------------------------------------------
:host(.hint-top-right),
:host(.hint-bottom-right) {
position: absolute;
right: 0;
border-width: globals.$ion-border-size-025;
border-style: globals.$ion-border-style-solid;
border-color: #fff;
}
:host(.hint-top-right) {
top: 0;
}
:host(.hint-bottom-right) {
bottom: 0;
}
// Hint Shapes
// --------------------------------------------------
/* Soft */
:host(.hint-soft) {
@include globals.border-radius(globals.$ion-border-radius-200);
}
:host(.hint-small.hint-soft) {
@include globals.border-radius(globals.$ion-border-radius-100);
}
/* Round */
:host(.hint-round) {
@include globals.border-radius(globals.$ion-border-radius-full);
}
/* Rectangular */
:host(.hint-rectangular) {
@include globals.border-radius(globals.$ion-border-radius-0);
}
// Hint Sizes
// --------------------------------------------------
/* Small status-hint */
:host(.hint-small) {
width: globals.$ion-scale-200;
height: globals.$ion-scale-200;
transform: translateX(-50%);
}
/* Small status-hint */
:host(.hint-medium) {
width: globals.$ion-scale-300;
height: globals.$ion-scale-300;
}
/* Large status-hint */
:host(.hint-large) {
width: globals.$ion-scale-400;
height: globals.$ion-scale-400;
}

View File

@@ -0,0 +1,89 @@
import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import { createColorClasses } from '@utils/theme';
import { getIonTheme } from '../../global/ionic-global';
import type { Color } from '../../interface';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
* @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
*/
@Component({
tag: 'ion-status-hint',
styleUrls: {
ionic: 'status-hint.ionic.scss',
},
shadow: true,
})
export class StatusHint implements ComponentInterface {
@Element() el!: HTMLElement;
@Prop({ reflect: true }) color?: Color;
/**
* Set to `"small"` for a compact size.
* Set to `"medium"` for the default height and width.
* Set to `"large"` for a larger size.
*
* Defaults to `"small"`.
*/
@Prop() size?: 'small' | 'medium' | 'large';
/**
* Set to `"soft"` for an hint with slightly rounded corners,
* `"round"` for an hint with fully rounded corners, or `"rectangular"`
* for an hint without rounded corners.
*
* Defaults to `"round"`.
*/
@Prop() shape?: 'soft' | 'round' | 'rectangular';
@Prop() position: 'top-right' | 'bottom-right' | 'static' = 'static';
private getSize(): string | undefined {
const { size } = this;
if (size === undefined) {
return 'large';
}
return size;
}
private getShape(): string | undefined {
const { shape } = this;
if (shape === undefined) {
return 'round';
}
return shape;
}
private getHintPosition(): string {
return this.position;
}
render() {
const theme = getIonTheme(this);
const size = this.getSize();
const shape = this.getShape();
const position = this.getHintPosition();
return (
<Host
class={createColorClasses(this.color, {
[theme]: true,
[theme]: true,
[`hint-${size}`]: size !== undefined,
[`hint-${shape}`]: shape !== undefined,
[`hint-${this.position}`]: position !== undefined,
})}
>
<slot></slot>
</Host>
);
}
}

View File

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Status Hint - Basic</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Status Hint - Basic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="content">
<ion-list>
<ion-list-header>
<ion-label>Isolated Status Hint</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Small</ion-label>
<ion-status-hint slot="end" color="primary" size="small"></ion-status-hint>
</ion-item>
<ion-item>
<ion-label>Medium</ion-label>
<ion-status-hint slot="end" color="warning" size="medium"></ion-status-hint>
</ion-item>
<ion-item>
<ion-label>Large (default)</ion-label>
<ion-status-hint slot="end" color="danger" size="large"></ion-status-hint>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-label>Status Hint in Avatar</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Top</ion-label>
<ion-avatar slot="end">
<ion-status-hint size="large" position="top-right" color="primary">1</ion-status-hint>
</ion-avatar>
</ion-item>
<ion-item>
<ion-label>Bottom</ion-label>
<ion-avatar slot="end">
<ion-status-hint size="large" position="bottom-right" color="primary">1</ion-status-hint>
</ion-avatar>
</ion-item>
</ion-list>
</ion-content>
</ion-app>
</body>
</html>

View File

@@ -77,6 +77,7 @@ export const DIRECTIVES = [
d.IonSkeletonText,
d.IonSpinner,
d.IonSplitPane,
d.IonStatusHint,
d.IonTab,
d.IonTabBar,
d.IonTabButton,

View File

@@ -261,14 +261,14 @@ export declare interface IonBackdrop extends Components.IonBackdrop {
@ProxyCmp({
inputs: ['color', 'mode', 'shape', 'size', 'theme']
inputs: ['color', 'hintPosition', 'mode', 'shape', 'size', 'theme', 'useAsHint']
})
@Component({
selector: 'ion-badge',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'mode', 'shape', 'size', 'theme'],
inputs: ['color', 'hintPosition', 'mode', 'shape', 'size', 'theme', 'useAsHint'],
})
export class IonBadge {
protected el: HTMLElement;
@@ -2230,6 +2230,28 @@ export declare interface IonSplitPane extends Components.IonSplitPane {
}
@ProxyCmp({
inputs: ['color', 'mode', 'position', 'shape', 'size', 'theme']
})
@Component({
selector: 'ion-status-hint',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'mode', 'position', 'shape', 'size', 'theme'],
})
export class IonStatusHint {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}
export declare interface IonStatusHint extends Components.IonStatusHint {}
@ProxyCmp({
inputs: ['component', 'mode', 'tab', 'theme'],
methods: ['setActive']

View File

@@ -72,6 +72,7 @@ import { defineCustomElement as defineIonSelectOption } from '@ionic/core/compon
import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js';
import { defineCustomElement as defineIonSpinner } from '@ionic/core/components/ion-spinner.js';
import { defineCustomElement as defineIonSplitPane } from '@ionic/core/components/ion-split-pane.js';
import { defineCustomElement as defineIonStatusHint } from '@ionic/core/components/ion-status-hint.js';
import { defineCustomElement as defineIonTab } from '@ionic/core/components/ion-tab.js';
import { defineCustomElement as defineIonTabBar } from '@ionic/core/components/ion-tab-bar.js';
import { defineCustomElement as defineIonTabButton } from '@ionic/core/components/ion-tab-button.js';
@@ -349,14 +350,14 @@ export declare interface IonBackdrop extends Components.IonBackdrop {
@ProxyCmp({
defineCustomElementFn: defineIonBadge,
inputs: ['color', 'mode', 'shape', 'size', 'theme']
inputs: ['color', 'hintPosition', 'mode', 'shape', 'size', 'theme', 'useAsHint']
})
@Component({
selector: 'ion-badge',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'mode', 'shape', 'size', 'theme'],
inputs: ['color', 'hintPosition', 'mode', 'shape', 'size', 'theme', 'useAsHint'],
standalone: true
})
export class IonBadge {
@@ -2030,6 +2031,30 @@ export declare interface IonSplitPane extends Components.IonSplitPane {
}
@ProxyCmp({
defineCustomElementFn: defineIonStatusHint,
inputs: ['color', 'mode', 'position', 'shape', 'size', 'theme']
})
@Component({
selector: 'ion-status-hint',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['color', 'mode', 'position', 'shape', 'size', 'theme'],
standalone: true
})
export class IonStatusHint {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}
export declare interface IonStatusHint extends Components.IonStatusHint {}
@ProxyCmp({
defineCustomElementFn: defineIonTab,
inputs: ['component', 'mode', 'tab', 'theme'],

View File

@@ -69,6 +69,7 @@ import { defineCustomElement as defineIonSelectOption } from '@ionic/core/compon
import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js';
import { defineCustomElement as defineIonSpinner } from '@ionic/core/components/ion-spinner.js';
import { defineCustomElement as defineIonSplitPane } from '@ionic/core/components/ion-split-pane.js';
import { defineCustomElement as defineIonStatusHint } from '@ionic/core/components/ion-status-hint.js';
import { defineCustomElement as defineIonTab } from '@ionic/core/components/ion-tab.js';
import { defineCustomElement as defineIonText } from '@ionic/core/components/ion-text.js';
import { defineCustomElement as defineIonTextarea } from '@ionic/core/components/ion-textarea.js';
@@ -141,6 +142,7 @@ export const IonSelectOption = /*@__PURE__*/createReactComponent<JSX.IonSelectOp
export const IonSkeletonText = /*@__PURE__*/createReactComponent<JSX.IonSkeletonText, HTMLIonSkeletonTextElement>('ion-skeleton-text', undefined, undefined, defineIonSkeletonText);
export const IonSpinner = /*@__PURE__*/createReactComponent<JSX.IonSpinner, HTMLIonSpinnerElement>('ion-spinner', undefined, undefined, defineIonSpinner);
export const IonSplitPane = /*@__PURE__*/createReactComponent<JSX.IonSplitPane, HTMLIonSplitPaneElement>('ion-split-pane', undefined, undefined, defineIonSplitPane);
export const IonStatusHint = /*@__PURE__*/createReactComponent<JSX.IonStatusHint, HTMLIonStatusHintElement>('ion-status-hint', undefined, undefined, defineIonStatusHint);
export const IonTab = /*@__PURE__*/createReactComponent<JSX.IonTab, HTMLIonTabElement>('ion-tab', undefined, undefined, defineIonTab);
export const IonText = /*@__PURE__*/createReactComponent<JSX.IonText, HTMLIonTextElement>('ion-text', undefined, undefined, defineIonText);
export const IonTextarea = /*@__PURE__*/createReactComponent<JSX.IonTextarea, HTMLIonTextareaElement>('ion-textarea', undefined, undefined, defineIonTextarea);

View File

@@ -75,6 +75,7 @@ import { defineCustomElement as defineIonSelectOption } from '@ionic/core/compon
import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js';
import { defineCustomElement as defineIonSpinner } from '@ionic/core/components/ion-spinner.js';
import { defineCustomElement as defineIonSplitPane } from '@ionic/core/components/ion-split-pane.js';
import { defineCustomElement as defineIonStatusHint } from '@ionic/core/components/ion-status-hint.js';
import { defineCustomElement as defineIonTab } from '@ionic/core/components/ion-tab.js';
import { defineCustomElement as defineIonText } from '@ionic/core/components/ion-text.js';
import { defineCustomElement as defineIonTextarea } from '@ionic/core/components/ion-textarea.js';
@@ -124,7 +125,9 @@ export const IonBackdrop = /*@__PURE__*/ defineContainer<JSX.IonBackdrop>('ion-b
export const IonBadge = /*@__PURE__*/ defineContainer<JSX.IonBadge>('ion-badge', defineIonBadge, [
'color',
'shape',
'size'
'size',
'useAsHint',
'hintPosition'
]);
@@ -848,6 +851,14 @@ export const IonSplitPane = /*@__PURE__*/ defineContainer<JSX.IonSplitPane>('ion
]);
export const IonStatusHint = /*@__PURE__*/ defineContainer<JSX.IonStatusHint>('ion-status-hint', defineIonStatusHint, [
'color',
'size',
'shape',
'position'
]);
export const IonTab = /*@__PURE__*/ defineContainer<JSX.IonTab>('ion-tab', defineIonTab, [
'active',
'delegate',