264 lines
4.9 KiB
TypeScript
264 lines
4.9 KiB
TypeScript
|
|
import {
|
||
|
|
DeesElement,
|
||
|
|
html,
|
||
|
|
property,
|
||
|
|
customElement,
|
||
|
|
css,
|
||
|
|
type TemplateResult,
|
||
|
|
} from '@design.estate/dees-element';
|
||
|
|
import * as lucideIcons from 'lucide';
|
||
|
|
import {
|
||
|
|
Activity,
|
||
|
|
ArrowUp,
|
||
|
|
Bell,
|
||
|
|
Bolt,
|
||
|
|
Box,
|
||
|
|
Building2,
|
||
|
|
Check,
|
||
|
|
ChevronDown,
|
||
|
|
ChevronRight,
|
||
|
|
Clock,
|
||
|
|
Cloud,
|
||
|
|
Copy,
|
||
|
|
CreditCard,
|
||
|
|
Fingerprint,
|
||
|
|
Globe,
|
||
|
|
Grid2x2,
|
||
|
|
Home,
|
||
|
|
Key,
|
||
|
|
Laptop,
|
||
|
|
Lock,
|
||
|
|
LogOut,
|
||
|
|
Mail,
|
||
|
|
MapPin,
|
||
|
|
Monitor,
|
||
|
|
MonitorSmartphone,
|
||
|
|
Nfc,
|
||
|
|
Phone,
|
||
|
|
Plus,
|
||
|
|
Power,
|
||
|
|
QrCode,
|
||
|
|
Search,
|
||
|
|
Settings,
|
||
|
|
Shield,
|
||
|
|
SmartphoneNfc,
|
||
|
|
SquarePen,
|
||
|
|
Trash2,
|
||
|
|
TriangleAlert,
|
||
|
|
User,
|
||
|
|
Users,
|
||
|
|
Wallet,
|
||
|
|
X,
|
||
|
|
createElement,
|
||
|
|
type IconNode,
|
||
|
|
} from 'lucide';
|
||
|
|
import { idpElementStyles } from './tokens.js';
|
||
|
|
|
||
|
|
export type TIdpIconName =
|
||
|
|
| 'activity'
|
||
|
|
| 'alert'
|
||
|
|
| 'alert-triangle'
|
||
|
|
| 'arrow-up'
|
||
|
|
| 'bell'
|
||
|
|
| 'bolt'
|
||
|
|
| 'box'
|
||
|
|
| 'building'
|
||
|
|
| 'building2'
|
||
|
|
| 'building-2'
|
||
|
|
| 'check'
|
||
|
|
| 'chevron'
|
||
|
|
| 'chevron-down'
|
||
|
|
| 'chevron-right'
|
||
|
|
| 'clock'
|
||
|
|
| 'cloud'
|
||
|
|
| 'copy'
|
||
|
|
| 'credit'
|
||
|
|
| 'device'
|
||
|
|
| 'edit'
|
||
|
|
| 'fingerprint'
|
||
|
|
| 'gear'
|
||
|
|
| 'globe'
|
||
|
|
| 'grid'
|
||
|
|
| 'home'
|
||
|
|
| 'key'
|
||
|
|
| 'laptop'
|
||
|
|
| 'location'
|
||
|
|
| 'lock'
|
||
|
|
| 'logout'
|
||
|
|
| 'mail'
|
||
|
|
| 'monitor'
|
||
|
|
| 'monitor-smartphone'
|
||
|
|
| 'nfc'
|
||
|
|
| 'phone'
|
||
|
|
| 'plus'
|
||
|
|
| 'power'
|
||
|
|
| 'qr'
|
||
|
|
| 'search'
|
||
|
|
| 'settings'
|
||
|
|
| 'shield'
|
||
|
|
| 'smartphone-nfc'
|
||
|
|
| 'trash'
|
||
|
|
| 'user'
|
||
|
|
| 'users'
|
||
|
|
| 'wallet'
|
||
|
|
| 'waveform'
|
||
|
|
| 'x'
|
||
|
|
| `lucide:${string}`;
|
||
|
|
|
||
|
|
declare global {
|
||
|
|
interface HTMLElementTagNameMap {
|
||
|
|
'idp-icon': IdpIcon;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const iconNodes: Record<string, IconNode> = {
|
||
|
|
activity: Activity,
|
||
|
|
alert: TriangleAlert,
|
||
|
|
'alert-triangle': TriangleAlert,
|
||
|
|
'arrow-up': ArrowUp,
|
||
|
|
bell: Bell,
|
||
|
|
bolt: Bolt,
|
||
|
|
box: Box,
|
||
|
|
building: Building2,
|
||
|
|
building2: Building2,
|
||
|
|
'building-2': Building2,
|
||
|
|
check: Check,
|
||
|
|
chevron: ChevronRight,
|
||
|
|
'chevron-down': ChevronDown,
|
||
|
|
'chevron-right': ChevronRight,
|
||
|
|
clock: Clock,
|
||
|
|
cloud: Cloud,
|
||
|
|
copy: Copy,
|
||
|
|
credit: CreditCard,
|
||
|
|
device: MonitorSmartphone,
|
||
|
|
edit: SquarePen,
|
||
|
|
fingerprint: Fingerprint,
|
||
|
|
gear: Settings,
|
||
|
|
globe: Globe,
|
||
|
|
grid: Grid2x2,
|
||
|
|
home: Home,
|
||
|
|
key: Key,
|
||
|
|
laptop: Laptop,
|
||
|
|
location: MapPin,
|
||
|
|
lock: Lock,
|
||
|
|
logout: LogOut,
|
||
|
|
mail: Mail,
|
||
|
|
monitor: Monitor,
|
||
|
|
'monitor-smartphone': MonitorSmartphone,
|
||
|
|
nfc: Nfc,
|
||
|
|
phone: Phone,
|
||
|
|
plus: Plus,
|
||
|
|
power: Power,
|
||
|
|
qr: QrCode,
|
||
|
|
search: Search,
|
||
|
|
settings: Settings,
|
||
|
|
shield: Shield,
|
||
|
|
'smartphone-nfc': SmartphoneNfc,
|
||
|
|
trash: Trash2,
|
||
|
|
user: User,
|
||
|
|
users: Users,
|
||
|
|
wallet: Wallet,
|
||
|
|
waveform: Activity,
|
||
|
|
x: X,
|
||
|
|
};
|
||
|
|
|
||
|
|
const toKebab = (valueArg: string): string => valueArg
|
||
|
|
.replace(/^lucide:/, '')
|
||
|
|
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
||
|
|
.toLowerCase();
|
||
|
|
|
||
|
|
const toLucideExportName = (valueArg: string): string => valueArg
|
||
|
|
.replace(/^lucide:/i, '')
|
||
|
|
.split(/[-_: ]+/)
|
||
|
|
.filter(Boolean)
|
||
|
|
.map((partArg) => `${partArg.charAt(0).toUpperCase()}${partArg.slice(1)}`)
|
||
|
|
.join('');
|
||
|
|
|
||
|
|
@customElement('idp-icon')
|
||
|
|
export class IdpIcon extends DeesElement {
|
||
|
|
public static demo = () => html`<idp-icon name="shield"></idp-icon>`;
|
||
|
|
public static demoGroups = ['idp.global v3 primitives'];
|
||
|
|
|
||
|
|
@property({ type: String })
|
||
|
|
public accessor name: TIdpIconName = 'shield';
|
||
|
|
|
||
|
|
@property({ type: Number })
|
||
|
|
public accessor size = 18;
|
||
|
|
|
||
|
|
private lastRenderKey = '';
|
||
|
|
|
||
|
|
public static styles = [
|
||
|
|
...idpElementStyles,
|
||
|
|
css`
|
||
|
|
:host {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
color: currentColor;
|
||
|
|
line-height: 0;
|
||
|
|
vertical-align: middle;
|
||
|
|
}
|
||
|
|
#iconContainer {
|
||
|
|
width: var(--icon-size);
|
||
|
|
height: var(--icon-size);
|
||
|
|
}
|
||
|
|
#iconContainer svg {
|
||
|
|
display: block;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
fill: none;
|
||
|
|
stroke: currentColor;
|
||
|
|
stroke-width: 1.75;
|
||
|
|
stroke-linecap: round;
|
||
|
|
stroke-linejoin: round;
|
||
|
|
}
|
||
|
|
`,
|
||
|
|
];
|
||
|
|
|
||
|
|
private resolveIconNode(): IconNode {
|
||
|
|
const rawName = String(this.name || 'shield');
|
||
|
|
const iconName = toKebab(rawName);
|
||
|
|
const aliasNode = iconNodes[iconName];
|
||
|
|
if (aliasNode) {
|
||
|
|
return aliasNode;
|
||
|
|
}
|
||
|
|
|
||
|
|
const exportName = toLucideExportName(rawName);
|
||
|
|
const lucideNode = (lucideIcons as Record<string, unknown>)[exportName];
|
||
|
|
if (Array.isArray(lucideNode)) {
|
||
|
|
return lucideNode as IconNode;
|
||
|
|
}
|
||
|
|
|
||
|
|
return Shield;
|
||
|
|
}
|
||
|
|
|
||
|
|
public render(): TemplateResult {
|
||
|
|
return html`
|
||
|
|
<div id="iconContainer" style="--icon-size: ${this.size}px"></div>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
public updated(): void {
|
||
|
|
const renderKey = `${this.name}:${this.size}`;
|
||
|
|
if (this.lastRenderKey === renderKey) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.lastRenderKey = renderKey;
|
||
|
|
|
||
|
|
const container = this.shadowRoot?.querySelector('#iconContainer');
|
||
|
|
if (!container) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
container.innerHTML = '';
|
||
|
|
const iconElement = createElement(this.resolveIconNode(), {
|
||
|
|
color: 'currentColor',
|
||
|
|
size: this.size,
|
||
|
|
strokeWidth: 1.75,
|
||
|
|
});
|
||
|
|
iconElement.setAttribute('aria-hidden', 'true');
|
||
|
|
container.appendChild(iconElement);
|
||
|
|
}
|
||
|
|
}
|