import {
booleanAttribute,
ChangeDetectionStrategy,
Component,
computed,
input,
output,
} from '@angular/core';
import type { IconName } from '@africanies/africanies-icons';
import { AfricaniesIconComponent } from '@africanies/africanies-icons';
/**
* Semantic tone for {@link ChipComponent}.
*
* Prefer these for common statuses. For one-off accents, pass {@link ChipComponent.color}
* instead — any CSS color with a faded background.
*
* - `neutral` — generic label / count
* - `success` — positive outcome (same green family as export)
* - `warning` — caution / pending
* - `danger` — error / exception
* - `info` — informational (theme blue)
* - `violet` / `teal` / `rose` / `cyan` — extra categorical tones
* - `export` — SFN / outbound accent
* - `import` — STN / inbound accent
*/
export type ChipVariant =
| 'neutral'
| 'success'
| 'warning'
| 'danger'
| 'info'
| 'violet'
| 'teal'
| 'rose'
| 'cyan'
| 'export'
| 'import';
/**
* Control size for {@link ChipComponent}.
*/
export type ChipSize = 'sm' | 'md';
/**
* Compact status / category label.
*
* Soft fills stay readable in dark mode (translucent accent tints, not
* light-only subtle backgrounds). Pass {@link color} for a custom accent —
* text uses that color and the background is a faded mix of the same.
*
* @example
* ```html
* Delivered
* Priority
* Pay Later
* Pending
* ```
*/
@Component({
selector: 'africanies-chip',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [AfricaniesIconComponent],
host: {
'[class]': 'hostClass()',
'[style.--africanies-chip-accent]': 'customAccent()',
role: 'status',
},
styles: `
:host.africanies-chip--custom {
color: var(--africanies-chip-accent);
background-color: color-mix(
in srgb,
var(--africanies-chip-accent) 14%,
transparent
);
border-color: color-mix(
in srgb,
var(--africanies-chip-accent) 28%,
transparent
);
}
:host-context(.dark):host.africanies-chip--custom {
/* Lighten the accent so labels stay readable on dark surfaces. */
color: color-mix(in srgb, var(--africanies-chip-accent) 68%, white);
background-color: color-mix(
in srgb,
var(--africanies-chip-accent) 22%,
transparent
);
border-color: color-mix(
in srgb,
var(--africanies-chip-accent) 40%,
transparent
);
}
`,
template: `
@if (icon(); as iconName) {
}
@if (removable()) {
}
`,
})
/**
* Compact status / category label.
*/
export class ChipComponent {
/** Semantic tone. Defaults to `neutral`. Ignored when {@link color} is set. */
readonly variant = input('neutral');
/**
* Optional CSS color (`#7c3aed`, `rgb(…)`, `oklch(…)`, …). When set, overrides
* {@link variant}: label uses the color and the fill is a faded mix of it.
*/
readonly color = input(null);
/** Padding / type scale. Defaults to `sm` (table / dense UI). */
readonly size = input('sm');
/** Optional leading icon. */
readonly icon = input(undefined);
/**
* When true, shows a remove control that emits {@link removed}. Parent owns
* list state — this component does not unmount itself.
*/
readonly removable = input(false, { transform: booleanAttribute });
/** Accessible label for the remove control. */
readonly removeLabel = input('Remove');
/** Emitted when the remove control is activated. */
readonly removed = output();
protected readonly iconSize = computed(() => (this.size() === 'md' ? 14 : 12));
/** Trimmed custom accent, or `null` when using a named {@link variant}. */
protected readonly customAccent = computed(() => {
const value = this.color()?.trim() ?? '';
return value.length > 0 ? value : null;
});
protected readonly hostClass = computed(() => {
const base =
'inline-flex max-w-full items-center gap-1 rounded-md font-sans font-medium ' +
'border border-transparent';
const sizes: Record = {
sm: 'px-2 py-0.5 text-caption',
md: 'px-2.5 py-1 text-body-sm',
};
return `${base} ${sizes[this.size()]} ${this.toneClass()}`;
});
/**
* Soft background + text per variant, or the custom-color host class.
* Dark mode uses translucent accents so chips never wash out as near-white
* blobs.
* @returns Tailwind / host class string for the active tone.
*/
private toneClass(): string {
if (this.customAccent()) {
return 'africanies-chip--custom';
}
switch (this.variant()) {
case 'success':
case 'export':
return 'bg-export-subtle text-export-strong dark:bg-export/15 dark:text-export-light';
case 'import':
return 'bg-import-subtle text-import-strong dark:bg-import/15 dark:text-import-light';
case 'warning':
return 'bg-warning-subtle text-warning-dark dark:bg-warning/15 dark:text-warning';
case 'danger':
return 'bg-danger-subtle text-danger-dark dark:bg-danger/15 dark:text-danger';
case 'info':
return 'bg-info-subtle text-info dark:bg-info/20 dark:text-[#93c5fd]';
case 'violet':
return 'bg-violet-100 text-violet-800 dark:bg-violet-400/15 dark:text-violet-300';
case 'teal':
return 'bg-teal-100 text-teal-800 dark:bg-teal-400/15 dark:text-teal-300';
case 'rose':
return 'bg-rose-100 text-rose-800 dark:bg-rose-400/15 dark:text-rose-300';
case 'cyan':
return 'bg-cyan-100 text-cyan-800 dark:bg-cyan-400/15 dark:text-cyan-300';
default:
return 'bg-border/60 text-ink dark:bg-white/10 dark:text-neutral-200';
}
}
/**
* @param event - Click on the remove control.
*/
protected onRemove(event: Event): void {
event.stopPropagation();
this.removed.emit();
}
}