diff --git a/fonts/signal-symbols/SignalSymbolsVariable.woff2 b/fonts/signal-symbols/SignalSymbolsVariable.woff2 index 24f696573c..a85ac3e1e5 100644 Binary files a/fonts/signal-symbols/SignalSymbolsVariable.woff2 and b/fonts/signal-symbols/SignalSymbolsVariable.woff2 differ diff --git a/ts/axo/AxoButton.tsx b/ts/axo/AxoButton.tsx index 0ba04943e5..69ea32068b 100644 --- a/ts/axo/AxoButton.tsx +++ b/ts/axo/AxoButton.tsx @@ -4,7 +4,7 @@ import React, { memo, forwardRef } from 'react'; import type { ButtonHTMLAttributes, FC, ForwardedRef, ReactNode } from 'react'; import type { TailwindStyles } from './tw.js'; import { tw } from './tw.js'; -import { AxoSymbol, type AxoSymbolName } from './AxoSymbol.js'; +import { AxoSymbol } from './AxoSymbol.js'; import { assert } from './_internal/assert.js'; const Namespace = 'AxoButton'; @@ -143,7 +143,7 @@ type AxoButtonProps = BaseButtonAttrs & Readonly<{ variant: AxoButtonVariant; size: AxoButtonSize; - symbol?: AxoSymbolName; + symbol?: AxoSymbol.InlineGlyphName; arrow?: boolean; children: ReactNode; }>; diff --git a/ts/axo/AxoSymbol.stories.tsx b/ts/axo/AxoSymbol.stories.tsx index d1ab21aed0..135da7ead3 100644 --- a/ts/axo/AxoSymbol.stories.tsx +++ b/ts/axo/AxoSymbol.stories.tsx @@ -4,34 +4,40 @@ import React, { memo, useMemo, useState } from 'react'; import type { Meta } from '@storybook/react'; import { Direction } from 'radix-ui'; import Fuse from 'fuse.js'; -import type { AxoSymbolName } from './AxoSymbol.js'; -import { - AxoSymbol, - _getAllAxoSymbolNames, - _getAxoSymbol, -} from './AxoSymbol.js'; +import { AxoSymbol } from './AxoSymbol.js'; import { tw } from './tw.js'; +import { + _getAllAxoSymbolInlineGlyphNames, + getAxoSymbolInlineGlyph, +} from './_internal/AxoSymbolDefs.generated.js'; export default { title: 'Axo/AxoSymbol', } satisfies Meta; -const SymbolInfo = memo(function SymbolInfo(props: { - symbolName: AxoSymbolName; -}): JSX.Element { - const ltr = _getAxoSymbol(props.symbolName, 'ltr'); - const rtl = _getAxoSymbol(props.symbolName, 'rtl'); +const allAxoSymbolNames = _getAllAxoSymbolInlineGlyphNames() + .slice() + .sort((a, b) => a.localeCompare(b)); +const fuse = new Fuse(allAxoSymbolNames); - const variants = +const SymbolInfo = memo(function SymbolInfo(props: { + symbolName: AxoSymbol.InlineGlyphName; +}): JSX.Element { + const ltr = getAxoSymbolInlineGlyph(props.symbolName, 'ltr'); + const rtl = getAxoSymbolInlineGlyph(props.symbolName, 'rtl'); + + type Variant = { title: string; dir: 'ltr' | 'rtl'; text: string }; + + const variants: ReadonlyArray = ltr === rtl - ? ([ + ? [ // same { title: 'LTR/RTL', dir: 'ltr', text: ltr }, - ] as const) - : ([ + ] + : [ { title: 'LTR', dir: 'ltr', text: ltr }, { title: 'RTL', dir: 'rtl', text: rtl }, - ] as const); + ]; return (
a.localeCompare(b)); -const fuse = new Fuse(allAxoSymbolNames); - export function All(): JSX.Element { const [input, setInput] = useState(''); diff --git a/ts/axo/AxoSymbol.tsx b/ts/axo/AxoSymbol.tsx index 7abd04144d..b1969cb993 100644 --- a/ts/axo/AxoSymbol.tsx +++ b/ts/axo/AxoSymbol.tsx @@ -1,241 +1,61 @@ // Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { FC } from 'react'; -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import { Direction } from 'radix-ui'; import { VisuallyHidden } from 'react-aria'; -import { assert } from './_internal/assert.js'; import { tw } from './tw.js'; +import { + getAxoSymbolIcon, + getAxoSymbolInlineGlyph, +} from './_internal/AxoSymbolDefs.generated.js'; +import type { + AxoSymbolIconName, + AxoSymbolInlineGlyphName, +} from './_internal/AxoSymbolDefs.generated.js'; const { useDirection } = Direction; const Namespace = 'AxoSymbol'; -type AxoSymbolDef = string | { ltr: string; rtl: string }; - -const AllAxoSymbolDefs = { - logo: '\u{E000}', - album: '\u{E001}', - appearance: '\u{E031}', - 'arrow-[start]': { ltr: '\u{2190}', rtl: '\u{2192}' }, - 'arrow-[end]': { ltr: '\u{2192}', rtl: '\u{2190}' }, - 'arrow-up': '\u{2191}', - 'arrow-down': '\u{2193}', - 'arrow-up_[start]': { ltr: '\u{2196}', rtl: '\u{2197}' }, - 'arrow-up_[end]': { ltr: '\u{2197}', rtl: '\u{2196}' }, - 'arrow-down_[start]': { ltr: '\u{2199}', rtl: '\u{2198}' }, - 'arrow-down_[end]': { ltr: '\u{2198}', rtl: '\u{2199}' }, - 'arrow-circle-[start]': { ltr: '\u{E00B}', rtl: '\u{E00C}' }, - 'arrow-circle-[end]': { ltr: '\u{E00C}', rtl: '\u{E00B}' }, - 'arrow-circle-up': '\u{E00D}', - 'arrow-circle-down': '\u{E00E}', - 'arrow-circle-up_[start]': { ltr: '\u{E00F}', rtl: '\u{E010}' }, - 'arrow-circle-up_[end]': { ltr: '\u{E010}', rtl: '\u{E00F}' }, - 'arrow-circle-down_[start]': { ltr: '\u{E011}', rtl: '\u{E012}' }, - 'arrow-circle-down_[end]': { ltr: '\u{E012}', rtl: '\u{E011}' }, - 'arrow-square-[start]': { ltr: '\u{E013}', rtl: '\u{E014}' }, - 'arrow-square-[end]': { ltr: '\u{E014}', rtl: '\u{E013}' }, - 'arrow-square-up': '\u{E015}', - 'arrow-square-down': '\u{E016}', - 'arrow-square-up_[start]': { ltr: '\u{E017}', rtl: '\u{E018}' }, - 'arrow-square-up_[end]': { ltr: '\u{E018}', rtl: '\u{E017}' }, - 'arrow-square-down_[start]': { ltr: '\u{E019}', rtl: '\u{E01A}' }, - 'arrow-square-down_[end]': { ltr: '\u{E01A}', rtl: '\u{E019}' }, - 'arrow-dash-down': '\u{E021}', - 'arrow-circle-[start]-fill': { ltr: '\u{E003}', rtl: '\u{E004}' }, - 'arrow-circle-[end]-fill': { ltr: '\u{E004}', rtl: '\u{E003}' }, - 'arrow-circle-up-fill': '\u{E005}', - 'arrow-circle-down-fill': '\u{E006}', - 'arrow-circle-up_[start]-fill': { ltr: '\u{E007}', rtl: '\u{E008}' }, - 'arrow-circle-up_[end]-fill': { ltr: '\u{E008}', rtl: '\u{E007}' }, - 'arrow-circle-down_[start]-fill': { ltr: '\u{E009}', rtl: '\u{E00A}' }, - 'arrow-circle-down_[end]-fill': { ltr: '\u{E00A}', rtl: '\u{E009}' }, - 'arrow-square-[start]-fill': { ltr: '\u{E08A}', rtl: '\u{E08B}' }, - 'arrow-square-[end]-fill': { ltr: '\u{E08B}', rtl: '\u{E08A}' }, - 'arrow-square-up-fill': '\u{E08C}', - 'arrow-square-down-fill': '\u{E08D}', - 'arrow-square-up_[start]-fill': { ltr: '\u{E08E}', rtl: '\u{E08F}' }, - 'arrow-square-up_[end]-fill': { ltr: '\u{E08F}', rtl: '\u{E08E}' }, - 'arrow-square-down_[start]-fill': { ltr: '\u{E090}', rtl: '\u{E091}' }, - 'arrow-square-down_[end]-fill': { ltr: '\u{E091}', rtl: '\u{E090}' }, - at: '\u{E01B}', - attach: '\u{E058}', - audio: '\u{E01C}', - 'audio-rectangle': '\u{E01D}', - badge: '\u{E099}', - 'badge-fill': '\u{E09A}', - bell: '\u{E01E}', - 'bell-slash': '\u{E01F}', - 'bell-ring': '\u{E020}', - block: '\u{E002}', - calender: '\u{E0A2}', - 'calender-blank': '\u{E0A3}', - check: '\u{2713}', - 'check-circle': '\u{E022}', - 'check-square': '\u{E023}', - 'chevron-[start]': { ltr: '\u{E024}', rtl: '\u{E025}' }, - 'chevron-[end]': { ltr: '\u{E025}', rtl: '\u{E024}' }, - 'chevron-up': '\u{E026}', - 'chevron-down': '\u{E027}', - 'chevron-circle-[start]': { ltr: '\u{E028}', rtl: '\u{E029}' }, - 'chevron-circle-[end]': { ltr: '\u{E029}', rtl: '\u{E028}' }, - 'chevron-circle-up': '\u{E02A}', - 'chevron-circle-down': '\u{E02B}', - 'chevron-square-[start]': { ltr: '\u{E02C}', rtl: '\u{E02D}' }, - 'chevron-square-[end]': { ltr: '\u{E02D}', rtl: '\u{E02C}' }, - 'chevron-square-up': '\u{E02E}', - 'chevron-square-down': '\u{E02F}', - 'dropdown-down': '\u{E07F}', - 'dropdown-up': '\u{E080}', - 'dropdown-triangle-down': '\u{E082}', - 'dropdown-triangle-up': '\u{E083}', - 'dropdown-double': '\u{E081}', - edit: '\u{E030}', - emoji: '\u{263A}', - error: '\u{E032}', - 'error-triangle': '\u{E092}', - 'error-fill': '\u{E093}', - 'error-triangle-fill': '\u{E094}', - file: '\u{E034}', - forward: '\u{E035}', - 'forward-fill': '\u{E036}', - gif: '\u{E037}', - 'gif-rectangle': '\u{E097}', - 'gif-rectangle-fill': '\u{E098}', - gift: '\u{E0B5}', - globe: '\u{E0B6}', - group: '\u{E038}', - 'group-x': '\u{E0AE}', - heart: '\u{E039}', - help: '\u{E0D8}', - incoming: '\u{E03A}', - info: '\u{E03B}', - leave: { ltr: '\u{E03C}', rtl: '\u{E03D}' }, - link: '\u{E03E}', - 'link-android': '\u{E03F}', - 'link-broken': '\u{E057}', - 'link-slash': '\u{E040}', - lock: '\u{E041}', - 'lock-open': '\u{E07D}', - megaphone: '\u{E042}', - merge: '\u{E043}', - message: '\u{E0A6}', - 'message_status-sending': '\u{E044}', - 'message_status-sent': '\u{E045}', - 'message_status-read': '\u{E047}', - 'message_status-delivered': '\u{E046}', - 'message_timer-00': '\u{E048}', - 'message_timer-05': '\u{E049}', - 'message_timer-10': '\u{E04A}', - 'message_timer-15': '\u{E04B}', - 'message_timer-20': '\u{E04C}', - 'message_timer-25': '\u{E04D}', - 'message_timer-30': '\u{E04E}', - 'message_timer-35': '\u{E04F}', - 'message_timer-40': '\u{E050}', - 'message_timer-45': '\u{E051}', - 'message_timer-50': '\u{E052}', - 'message_timer-55': '\u{E053}', - 'message_timer-60': '\u{E054}', - mic: '\u{E055}', - 'mic-slash': '\u{E056}', - minus: '\u{2212}', - 'minus-circle': '\u{2296}', - 'minus-square': '\u{E059}', - 'missed-incoming': '\u{E05A}', - 'missed-outgoing': '\u{E05B}', - note: { ltr: '\u{E095}', rtl: '\u{E096}' }, - official_badge: '\u{E086}', - 'official_badge-fill': '\u{E087}', - outgoing: '\u{E05C}', - person: '\u{E05D}', - 'person-circle': '\u{E05E}', - 'person-check': '\u{E05F}', - 'person-x': '\u{E060}', - 'person-plus': '\u{E061}', - 'person-minus': '\u{E062}', - 'person-question': '\u{E06A}', - phone: '\u{E063}', - 'phone-fill': '\u{E064}', - photo: '\u{E065}', - 'photo-slash': '\u{E066}', - play: '\u{E067}', - 'play-circle': '\u{E068}', - 'play-square': '\u{E069}', - plus: '\u{002B}', - 'plus-circle': '\u{2295}', - 'plus-square': '\u{E06C}', - raise_hand: '\u{E07E}', - 'raise_hand-fill': '\u{E084}', - refresh: '\u{E0C4}', - reply: '\u{E06D}', - 'reply-fill': '\u{E06E}', - safety_number: '\u{E06F}', - spam: '\u{E033}', - sticker: '\u{E070}', - thread: '\u{E071}', - 'thread-fill': '\u{E072}', - timer: '\u{E073}', - 'timer-slash': '\u{E074}', - video_camera: '\u{E075}', - 'video_camera-slash': '\u{E076}', - 'video_camera-fill': '\u{E077}', - video: '\u{E088}', - 'video-slash': '\u{E089}', - view_once: '\u{E078}', - 'view_once-dash': '\u{E079}', - 'view_once-viewed': '\u{E07A}', - x: '\u{00D7}', - 'x-circle': '\u{2297}', - 'x-square': '\u{2327}', - space: '\u{0020}', -} as const satisfies Record; - -export type AxoSymbolName = keyof typeof AllAxoSymbolDefs; - -export function _getAllAxoSymbolNames(): ReadonlyArray { - return Object.keys(AllAxoSymbolDefs) as Array; -} - -export function _getAxoSymbol( - symbolName: AxoSymbolName, - dir: 'ltr' | 'rtl' -): string { - const symbolDef = assert( - AllAxoSymbolDefs[symbolName], - `${Namespace}:Invalid name: ${symbolName}` - ); - const symbol = typeof symbolDef === 'string' ? symbolDef : symbolDef[dir]; - return symbol; -} - // eslint-disable-next-line @typescript-eslint/no-namespace export namespace AxoSymbol { + const symbolStyles = tw('font-symbols select-none'); + const labelStyles = tw('select-none'); + + // eslint-disable-next-line no-inner-declarations + function useRenderSymbol(glyph: string, label: string | null): JSX.Element { + return useMemo(() => { + return ( + <> + + {glyph} + + {label != null && ( + {label} + )} + + ); + }, [glyph, label]); + } + /** * Component: * -------------------------------------- */ + export type InlineGlyphName = AxoSymbolInlineGlyphName; + export type InlineGlyphProps = Readonly<{ - symbol: AxoSymbolName; + symbol: InlineGlyphName; label: string | null; }>; export const InlineGlyph: FC = memo(props => { const direction = useDirection(); - const symbol = _getAxoSymbol(props.symbol, direction); - return ( - <> - - {symbol} - - {props.label != null && ( - - {props.label} - - )} - - ); + const glyph = getAxoSymbolInlineGlyph(props.symbol, direction); + const content = useRenderSymbol(glyph, props.label); + return content; }); InlineGlyph.displayName = `${Namespace}.InlineGlyph`; @@ -245,21 +65,50 @@ export namespace AxoSymbol { * -------------------------------------- */ + export type IconName = AxoSymbolIconName; + export type IconSize = 14 | 16 | 20 | 24; + + type IconSizeConfig = { size: number; fontSize: number }; + + const IconSizes: Record = { + 14: { size: 14, fontSize: 12 }, + 16: { size: 16, fontSize: 14 }, + 20: { size: 20, fontSize: 18 }, + 24: { size: 24, fontSize: 22 }, + }; + + export function _getAllIconSizes(): ReadonlyArray { + return Object.keys(IconSizes).map(size => Number(size) as IconSize); + } + export type IconProps = Readonly<{ - size: 14 | 16 | 20 | 24; - symbol: AxoSymbolName; + size: IconSize; + symbol: AxoSymbolIconName; label: string | null; }>; + const iconStyles = tw( + 'inline-flex size-[1em] shrink-0 items-center justify-center' + ); + export const Icon: FC = memo(props => { + const config = IconSizes[props.size]; + + const direction = useDirection(); + const glyph = getAxoSymbolIcon(props.symbol, direction); + const content = useRenderSymbol(glyph, props.label); + + const style = useMemo(() => { + return { + width: config.size, + height: config.size, + fontSize: config.fontSize, + }; + }, [config]); + return ( - - + + {content} ); }); diff --git a/ts/axo/_internal/AxoBaseMenu.tsx b/ts/axo/_internal/AxoBaseMenu.tsx index 3aebe771fa..a005769937 100644 --- a/ts/axo/_internal/AxoBaseMenu.tsx +++ b/ts/axo/_internal/AxoBaseMenu.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type { ReactNode } from 'react'; import { tw } from '../tw.js'; -import { AxoSymbol, type AxoSymbolName } from '../AxoSymbol.js'; +import { AxoSymbol } from '../AxoSymbol.js'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace AxoBaseMenu { @@ -62,7 +62,7 @@ export namespace AxoBaseMenu { /** * An icon that should be rendered before the text. */ - symbol?: AxoSymbolName; + symbol?: AxoSymbol.IconName; }>; // (not SubTrigger/Label/Separator) @@ -138,7 +138,9 @@ export namespace AxoBaseMenu { return ; } - export function ItemSymbol(props: { symbol: AxoSymbolName }): JSX.Element { + export function ItemSymbol(props: { + symbol: AxoSymbol.IconName; + }): JSX.Element { return ; } diff --git a/ts/axo/_internal/AxoSymbolDefs.generated.ts b/ts/axo/_internal/AxoSymbolDefs.generated.ts new file mode 100644 index 0000000000..bfed299738 --- /dev/null +++ b/ts/axo/_internal/AxoSymbolDefs.generated.ts @@ -0,0 +1,820 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +// WARNING: This file is automatically generated, do not edit directly + +// Can be used in +export type AxoSymbolIconName = + | 'signal-logo' + | 'album' + | 'appearance' + | 'appearance-fill' + | 'archive' + | 'archive-fill' + | 'archive-down' + | 'archive-down-fill' + | 'archive-up' + | 'archive-up-fill' + | 'arrow-[start]' + | 'arrow-[end]' + | 'arrow-up' + | 'arrow-down' + | 'arrow-up[start]' + | 'arrow-up[end]' + | 'arrow-down[start]' + | 'arrow-down[end]' + | 'arrow-circle-[start]' + | 'arrow-circle-[end]' + | 'arrow-circle-up' + | 'arrow-circle-down' + | 'arrow-circle-up[start]' + | 'arrow-circle-up[end]' + | 'arrow-circle-down[start]' + | 'arrow-circle-down[end]' + | 'arrow-circle-[start]-fill' + | 'arrow-circle-[end]-fill' + | 'arrow-circle-up-fill' + | 'arrow-circle-down-fill' + | 'arrow-circle-up[start]-fill' + | 'arrow-circle-up[end]-fill' + | 'arrow-circle-down[start]-fill' + | 'arrow-circle-down[end]-fill' + | 'arrow-square-[start]' + | 'arrow-square-[end]' + | 'arrow-square-up' + | 'arrow-square-down' + | 'arrow-square-up[start]' + | 'arrow-square-up[end]' + | 'arrow-square-down[start]' + | 'arrow-square-down[end]' + | 'arrow-square-[start]-fill' + | 'arrow-square-[end]-fill' + | 'arrow-square-up-fill' + | 'arrow-square-down-fill' + | 'arrow-square-up[start]-fill' + | 'arrow-square-up[end]-fill' + | 'arrow-square-down[start]-fill' + | 'arrow-square-down[end]-fill' + | 'arrow-rectangle-up' + | 'arrow-rectangle-up-fill' + | 'arrow-circle-dash-down' + | 'aspectratio' + | 'aspectratio-fill' + | 'at' + | 'attach' + | 'audio' + | 'audio-rectangle' + | 'audio-rectangle-fill' + | 'backup' + | 'badge' + | 'badge-fill' + | 'badge-set' + | 'badge-set-fill' + | 'bell' + | 'bell-slash' + | 'bell-ring' + | 'block' + | 'bolt' + | 'boost' + | 'calendar' + | 'calendar-blank' + | 'calendar-week' + | 'calendar-one' + | 'calendar-search' + | 'camera' + | 'camera-fill' + | 'check' + | 'check-circle' + | 'check-circle-fill' + | 'check-square' + | 'check-square-fill' + | 'chevron-[start]' + | 'chevron-[end]' + | 'chevron-up' + | 'chevron-down' + | 'chevron-circle-[start]' + | 'chevron-circle-[end]' + | 'chevron-circle-up' + | 'chevron-circle-down' + | 'chevron-circle-[start]-fill' + | 'chevron-circle-[end]-fill' + | 'chevron-circle-up-fill' + | 'chevron-circle-down-fill' + | 'chevron-square-[start]' + | 'chevron-square-[end]' + | 'chevron-square-up' + | 'chevron-square-down' + | 'chevron-square-[start]-fill' + | 'chevron-square-[end]-fill' + | 'chevron-square-up-fill' + | 'chevron-square-down-fill' + | 'chevron-shallow-[start]' + | 'chevron-shallow-[end]' + | 'chevron-shallow-up' + | 'chevron-shallow-down' + | 'chevron-double-left-right' + | 'chevron-double-up-down' + | 'circle' + | 'circle-fill' + | 'circle-dash' + | 'compose' + | 'connections' + | 'connections-fill' + | 'copy' + | 'copy-alt' + | 'countdown-00' + | 'countdown-05' + | 'countdown-10' + | 'countdown-15' + | 'countdown-20' + | 'countdown-25' + | 'countdown-30' + | 'countdown-35' + | 'countdown-40' + | 'countdown-45' + | 'countdown-50' + | 'countdown-55' + | 'countdown-60' + | 'creditcard' + | 'creditcard-fill' + | 'crop' + | 'crop-rotate' + | 'download' + | 'draghandle' + | 'draghandle-alt' + | 'emoji' + | 'emoji-fill' + | 'emoji-excited' + | 'emoji-sad' + | 'emoji-angry' + | 'emoji-thumbsup' + | 'emoji-surprised' + | 'emoji-animal' + | 'emoji-celebrate' + | 'emoji-food' + | 'emoji-activity' + | 'emoji-travel' + | 'emoji-object' + | 'emoji-symbol' + | 'emoji-flag' + | 'emoticon' + | 'error' + | 'error-fill' + | 'error-triangle' + | 'error-triangle-fill' + | 'error-octagon' + | 'error-octagon-fill' + | 'file' + | 'file-fill' + | 'file-slash' + | 'file-slash-fill' + | 'filter' + | 'filter-circle' + | 'filter-circle-fill' + | 'folder' + | 'forward' + | 'forward-fill' + | 'fullscreen' + | 'gif' + | 'gif-rectangle' + | 'gif-rectangle-fill' + | 'gift' + | 'globe' + | 'grid' + | 'grid-fill' + | 'grid-rectangle' + | 'grid-rectangle-fill' + | 'group' + | 'group-fill' + | 'heart' + | 'heart-plus' + | 'heart-fill' + | 'heart-plus-fill' + | 'help' + | 'help-fill' + | 'info' + | 'info-fill' + | 'invite' + | 'key' + | 'key-slash' + | 'leave' + | 'leave-rtl' + | 'link' + | 'link-alt' + | 'link-broken' + | 'link-slash' + | 'list-bullet' + | 'list-bullet-rtl' + | 'list-circle' + | 'list-circle-rtl' + | 'lock' + | 'lock-open' + | 'lock-fill' + | 'lock-open-fill' + | 'megaphone' + | 'menu' + | 'merge' + | 'message' + | 'message-badge' + | 'message-arrow' + | 'message-check' + | 'message-more' + | 'message-x' + | 'message-fill' + | 'message-badge-fill' + | 'message-arrow-fill' + | 'message-check-fill' + | 'message-more-fill' + | 'message-x-fill' + | 'mic' + | 'mic-fill' + | 'mic-slash' + | 'mic-slash-fill' + | 'minus' + | 'minus-circle' + | 'minus-square' + | 'minus-circle-fill' + | 'minus-square-fill' + | 'missed-incoming' + | 'missed-outgoing' + | 'moon' + | 'moon-slash' + | 'moon-fill' + | 'moon-slash-fill' + | 'more' + | 'more-circle' + | 'more-circle-fill' + | 'note' + | 'note-rtl' + | 'number' + | 'number-square' + | 'number-square-fill' + | 'officialbadge' + | 'officialbadge-fill' + | 'open' + | 'palette' + | 'palette-fill' + | 'pencil' + | 'pencil-fill' + | 'person' + | 'person-fill' + | 'person-circle' + | 'person-square' + | 'person-rectangle' + | 'phone' + | 'phone-fill' + | 'photo' + | 'photo-slash' + | 'piechart' + | 'piechart-fill' + | 'pin' + | 'pin-fill' + | 'pin-slash' + | 'pin-slash-fill' + | 'play' + | 'play-circle' + | 'play-square' + | 'play-fill' + | 'play-circle-fill' + | 'play-square-fill' + | 'plus' + | 'plus-circle' + | 'plus-square' + | 'plus-circle-fill' + | 'plus-square-fill' + | 'poll' + | 'poll-fill' + | 'qrcode' + | 'raisehand' + | 'raisehand-fill' + | 'receipt' + | 'receipt-rtl' + | 'recent' + | 'rectangle' + | 'rectangle-fill' + | 'redo' + | 'refresh' + | 'reply' + | 'reply-fill' + | 'rotate' + | 'scan' + | 'scribble' + | 'search' + | 'send' + | 'send-fill' + | 'settings' + | 'settings-fill' + | 'shield-check' + | 'shield-check-fill' + | 'square' + | 'square-fill' + | 'star' + | 'star-fill' + | 'sticker' + | 'stories' + | 'stories-fill' + | 'sun' + | 'sun-fill' + | 'sun-horizon' + | 'sun-horizon-fill' + | 'swap' + | 'thread' + | 'thread-fill' + | 'ticks' + | 'timer' + | 'timer-fill' + | 'timer-slash' + | 'timer-slash-fill' + | 'trash' + | 'trash-fill' + | 'tune' + | 'undo' + | 'upload' + | 'video' + | 'video-slash' + | 'video-fill' + | 'video-slash-fill' + | 'videocamera' + | 'videocamera-fill' + | 'videocamera-slash' + | 'videocamera-slash-fill' + | 'viewonce' + | 'viewonce-dash' + | 'wifi' + | 'x' + | 'x-circle' + | 'x-circle-fill' + | 'x-circle-dash' + | 'x-square' + | 'x-square-fill'; + +// Symbols that can only be used in +type AxoSymbolInlineGlyphOnlyName = + | 'check-double-circle' + | 'check-double-circle-fill' + | 'group-x' + | 'group-x-fill' + | 'person-check' + | 'person-plus' + | 'person-minus' + | 'person-x' + | 'person-question'; + +// Symbols with an inline-specific glyph that override the icon glyph +type AxoSymbolInlineGlyphOverrideName = + | 'album' + | 'arrow-rectangle-up' + | 'arrow-rectangle-up-fill' + | 'aspectratio' + | 'aspectratio-fill' + | 'audio-rectangle' + | 'audio-rectangle-fill' + | 'gif-rectangle' + | 'gif-rectangle-fill' + | 'grid-rectangle' + | 'grid-rectangle-fill' + | 'group' + | 'group-fill' + | 'invite' + | 'leave' + | 'leave-rtl' + | 'person-rectangle' + | 'photo' + | 'photo-slash' + | 'rectangle' + | 'rectangle-fill' + | 'video' + | 'video-slash' + | 'video-fill' + | 'video-slash-fill' + | 'videocamera' + | 'videocamera-fill' + | 'videocamera-slash' + | 'videocamera-slash-fill'; + +// Symbols that can be used in +export type AxoSymbolInlineGlyphName = + | AxoSymbolIconName + | AxoSymbolInlineGlyphOnlyName; + +type SymbolDef = string | { ltr: string; rtl: string }; +type IconDefsName = AxoSymbolIconName; +type InlineDefsName = + | AxoSymbolInlineGlyphOnlyName + | AxoSymbolInlineGlyphOverrideName; + +const IconDefs: Record = { + 'signal-logo': '\u{E000}', + album: '\u{E163}', + appearance: '\u{E031}', + 'appearance-fill': '\u{E164}', + archive: '\u{E09B}', + 'archive-fill': '\u{E165}', + 'archive-down': '\u{E205}', + 'archive-down-fill': '\u{E206}', + 'archive-up': '\u{E09C}', + 'archive-up-fill': '\u{E166}', + 'arrow-[start]': { ltr: '\u{E169}', rtl: '\u{E16A}' }, + 'arrow-[end]': { ltr: '\u{E16A}', rtl: '\u{E169}' }, + 'arrow-up': '\u{E16B}', + 'arrow-down': '\u{E16C}', + 'arrow-up[start]': { ltr: '\u{E16D}', rtl: '\u{E16E}' }, + 'arrow-up[end]': { ltr: '\u{E16E}', rtl: '\u{E16D}' }, + 'arrow-down[start]': { ltr: '\u{E16F}', rtl: '\u{E170}' }, + 'arrow-down[end]': { ltr: '\u{E170}', rtl: '\u{E16F}' }, + 'arrow-circle-[start]': { ltr: '\u{E00B}', rtl: '\u{E00C}' }, + 'arrow-circle-[end]': { ltr: '\u{E00C}', rtl: '\u{E00B}' }, + 'arrow-circle-up': '\u{E00D}', + 'arrow-circle-down': '\u{E00E}', + 'arrow-circle-up[start]': { ltr: '\u{E00F}', rtl: '\u{E010}' }, + 'arrow-circle-up[end]': { ltr: '\u{E010}', rtl: '\u{E00F}' }, + 'arrow-circle-down[start]': { ltr: '\u{E011}', rtl: '\u{E012}' }, + 'arrow-circle-down[end]': { ltr: '\u{E012}', rtl: '\u{E011}' }, + 'arrow-circle-[start]-fill': { ltr: '\u{E003}', rtl: '\u{E004}' }, + 'arrow-circle-[end]-fill': { ltr: '\u{E004}', rtl: '\u{E003}' }, + 'arrow-circle-up-fill': '\u{E005}', + 'arrow-circle-down-fill': '\u{E006}', + 'arrow-circle-up[start]-fill': { ltr: '\u{E007}', rtl: '\u{E008}' }, + 'arrow-circle-up[end]-fill': { ltr: '\u{E008}', rtl: '\u{E007}' }, + 'arrow-circle-down[start]-fill': { ltr: '\u{E009}', rtl: '\u{E00A}' }, + 'arrow-circle-down[end]-fill': { ltr: '\u{E00A}', rtl: '\u{E009}' }, + 'arrow-square-[start]': { ltr: '\u{E013}', rtl: '\u{E014}' }, + 'arrow-square-[end]': { ltr: '\u{E014}', rtl: '\u{E013}' }, + 'arrow-square-up': '\u{E015}', + 'arrow-square-down': '\u{E016}', + 'arrow-square-up[start]': { ltr: '\u{E017}', rtl: '\u{E018}' }, + 'arrow-square-up[end]': { ltr: '\u{E018}', rtl: '\u{E017}' }, + 'arrow-square-down[start]': { ltr: '\u{E019}', rtl: '\u{E01A}' }, + 'arrow-square-down[end]': { ltr: '\u{E01A}', rtl: '\u{E019}' }, + 'arrow-square-[start]-fill': { ltr: '\u{E08A}', rtl: '\u{E08B}' }, + 'arrow-square-[end]-fill': { ltr: '\u{E08B}', rtl: '\u{E08A}' }, + 'arrow-square-up-fill': '\u{E08C}', + 'arrow-square-down-fill': '\u{E08D}', + 'arrow-square-up[start]-fill': { ltr: '\u{E08E}', rtl: '\u{E08F}' }, + 'arrow-square-up[end]-fill': { ltr: '\u{E08F}', rtl: '\u{E08E}' }, + 'arrow-square-down[start]-fill': { ltr: '\u{E090}', rtl: '\u{E091}' }, + 'arrow-square-down[end]-fill': { ltr: '\u{E091}', rtl: '\u{E090}' }, + 'arrow-rectangle-up': '\u{E0CD}', + 'arrow-rectangle-up-fill': '\u{E173}', + 'arrow-circle-dash-down': '\u{E172}', + aspectratio: '\u{E134}', + 'aspectratio-fill': '\u{E176}', + at: '\u{E01B}', + attach: '\u{E058}', + audio: '\u{E01C}', + 'audio-rectangle': '\u{E178}', + 'audio-rectangle-fill': '\u{E179}', + backup: '\u{E09F}', + badge: '\u{E099}', + 'badge-fill': '\u{E09A}', + 'badge-set': '\u{E0DA}', + 'badge-set-fill': '\u{E17D}', + bell: '\u{E01E}', + 'bell-slash': '\u{E01F}', + 'bell-ring': '\u{E020}', + block: '\u{E002}', + bolt: '\u{E0B8}', + boost: '\u{E0E2}', + calendar: '\u{E0A2}', + 'calendar-blank': '\u{E0A3}', + 'calendar-week': '\u{E0A4}', + 'calendar-one': '\u{E0A5}', + 'calendar-search': '\u{E0E3}', + camera: '\u{E0E4}', + 'camera-fill': '\u{E17E}', + check: '\u{E180}', + 'check-circle': '\u{E022}', + 'check-circle-fill': '\u{E182}', + 'check-square': '\u{E023}', + 'check-square-fill': '\u{E183}', + 'chevron-[start]': { ltr: '\u{E024}', rtl: '\u{E025}' }, + 'chevron-[end]': { ltr: '\u{E025}', rtl: '\u{E024}' }, + 'chevron-up': '\u{E026}', + 'chevron-down': '\u{E027}', + 'chevron-circle-[start]': { ltr: '\u{E028}', rtl: '\u{E029}' }, + 'chevron-circle-[end]': { ltr: '\u{E029}', rtl: '\u{E028}' }, + 'chevron-circle-up': '\u{E02A}', + 'chevron-circle-down': '\u{E02B}', + 'chevron-circle-[start]-fill': { ltr: '\u{E1F2}', rtl: '\u{E1F3}' }, + 'chevron-circle-[end]-fill': { ltr: '\u{E1F3}', rtl: '\u{E1F2}' }, + 'chevron-circle-up-fill': '\u{E1F4}', + 'chevron-circle-down-fill': '\u{E1F5}', + 'chevron-square-[start]': { ltr: '\u{E02C}', rtl: '\u{E02D}' }, + 'chevron-square-[end]': { ltr: '\u{E02D}', rtl: '\u{E02C}' }, + 'chevron-square-up': '\u{E02E}', + 'chevron-square-down': '\u{E02F}', + 'chevron-square-[start]-fill': { ltr: '\u{E1F6}', rtl: '\u{E1F7}' }, + 'chevron-square-[end]-fill': { ltr: '\u{E1F7}', rtl: '\u{E1F6}' }, + 'chevron-square-up-fill': '\u{E1F8}', + 'chevron-square-down-fill': '\u{E1F9}', + 'chevron-shallow-[start]': { ltr: '\u{E0E6}', rtl: '\u{E0E7}' }, + 'chevron-shallow-[end]': { ltr: '\u{E0E7}', rtl: '\u{E0E6}' }, + 'chevron-shallow-up': '\u{E0E8}', + 'chevron-shallow-down': '\u{E0E9}', + 'chevron-double-left-right': '\u{E207}', + 'chevron-double-up-down': '\u{E081}', + circle: '\u{E160}', + 'circle-fill': '\u{E184}', + 'circle-dash': '\u{E07A}', + compose: '\u{E0EA}', + connections: '\u{E0AD}', + 'connections-fill': '\u{E185}', + copy: '\u{E0EB}', + 'copy-alt': '\u{E0EC}', + 'countdown-00': '\u{E048}', + 'countdown-05': '\u{E049}', + 'countdown-10': '\u{E04A}', + 'countdown-15': '\u{E04B}', + 'countdown-20': '\u{E04C}', + 'countdown-25': '\u{E04D}', + 'countdown-30': '\u{E04E}', + 'countdown-35': '\u{E04F}', + 'countdown-40': '\u{E050}', + 'countdown-45': '\u{E051}', + 'countdown-50': '\u{E052}', + 'countdown-55': '\u{E053}', + 'countdown-60': '\u{E054}', + creditcard: '\u{E127}', + 'creditcard-fill': '\u{E187}', + crop: '\u{E0ED}', + 'crop-rotate': '\u{E0EE}', + download: '\u{E0C8}', + draghandle: '\u{E0F5}', + 'draghandle-alt': '\u{E0F6}', + emoji: '\u{E18D}', + 'emoji-fill': '\u{E18E}', + 'emoji-excited': '\u{E0F9}', + 'emoji-sad': '\u{E0FA}', + 'emoji-angry': '\u{E0FB}', + 'emoji-thumbsup': '\u{E0FC}', + 'emoji-surprised': '\u{E0FD}', + 'emoji-animal': '\u{E0FE}', + 'emoji-celebrate': '\u{E0FF}', + 'emoji-food': '\u{E100}', + 'emoji-activity': '\u{E101}', + 'emoji-travel': '\u{E102}', + 'emoji-object': '\u{E103}', + 'emoji-symbol': '\u{E104}', + 'emoji-flag': '\u{E105}', + emoticon: '\u{E106}', + error: '\u{E032}', + 'error-fill': '\u{E093}', + 'error-triangle': '\u{E092}', + 'error-triangle-fill': '\u{E094}', + 'error-octagon': '\u{E033}', + 'error-octagon-fill': '\u{E18F}', + file: '\u{E034}', + 'file-fill': '\u{E190}', + 'file-slash': '\u{E0B1}', + 'file-slash-fill': '\u{E191}', + filter: '\u{E107}', + 'filter-circle': '\u{E108}', + 'filter-circle-fill': '\u{E1FA}', + folder: '\u{E0B2}', + forward: '\u{E035}', + 'forward-fill': '\u{E036}', + fullscreen: '\u{E10D}', + gif: '\u{E037}', + 'gif-rectangle': '\u{E195}', + 'gif-rectangle-fill': '\u{E196}', + gift: '\u{E0B5}', + globe: '\u{E0B6}', + grid: '\u{E10E}', + 'grid-fill': '\u{E198}', + 'grid-rectangle': '\u{E10F}', + 'grid-rectangle-fill': '\u{E199}', + group: '\u{E19B}', + 'group-fill': '\u{E19D}', + heart: '\u{E039}', + 'heart-plus': '\u{E0B7}', + 'heart-fill': '\u{E1A4}', + 'heart-plus-fill': '\u{E1A5}', + help: '\u{E0D8}', + 'help-fill': '\u{E1A6}', + info: '\u{E03B}', + 'info-fill': '\u{E1A7}', + invite: '\u{E0B9}', + key: '\u{E0BA}', + 'key-slash': '\u{E0BB}', + leave: '\u{E1AA}', + 'leave-rtl': '\u{E1AB}', + link: '\u{E03E}', + 'link-alt': '\u{E03F}', + 'link-broken': '\u{E057}', + 'link-slash': '\u{E040}', + 'list-bullet': '\u{E113}', + 'list-bullet-rtl': '\u{E115}', + 'list-circle': '\u{E114}', + 'list-circle-rtl': '\u{E116}', + lock: '\u{E041}', + 'lock-open': '\u{E07D}', + 'lock-fill': '\u{E1AD}', + 'lock-open-fill': '\u{E1AE}', + megaphone: '\u{E042}', + menu: '\u{E11B}', + merge: '\u{E043}', + message: '\u{E0A6}', + 'message-badge': '\u{E0A7}', + 'message-arrow': '\u{E0A8}', + 'message-check': '\u{E0A9}', + 'message-more': '\u{E0AA}', + 'message-x': '\u{E0AB}', + 'message-fill': '\u{E1AF}', + 'message-badge-fill': '\u{E1B0}', + 'message-arrow-fill': '\u{E1B1}', + 'message-check-fill': '\u{E1B2}', + 'message-more-fill': '\u{E1B3}', + 'message-x-fill': '\u{E1B4}', + mic: '\u{E055}', + 'mic-fill': '\u{E1B5}', + 'mic-slash': '\u{E056}', + 'mic-slash-fill': '\u{E1B6}', + minus: '\u{E1B7}', + 'minus-circle': '\u{E1B8}', + 'minus-square': '\u{E059}', + 'minus-circle-fill': '\u{E1B9}', + 'minus-square-fill': '\u{E1BA}', + 'missed-incoming': '\u{E05A}', + 'missed-outgoing': '\u{E05B}', + moon: '\u{E0BE}', + 'moon-slash': '\u{E209}', + 'moon-fill': '\u{E0D9}', + 'moon-slash-fill': '\u{E20A}', + more: '\u{E120}', + 'more-circle': '\u{E121}', + 'more-circle-fill': '\u{E208}', + note: '\u{E095}', + 'note-rtl': '\u{E096}', + number: '\u{E0BF}', + 'number-square': '\u{E0C0}', + 'number-square-fill': '\u{E1BC}', + officialbadge: '\u{E086}', + 'officialbadge-fill': '\u{E087}', + open: '\u{E0C1}', + palette: '\u{E0AC}', + 'palette-fill': '\u{E1BD}', + pencil: '\u{E030}', + 'pencil-fill': '\u{E1C1}', + person: '\u{E05D}', + 'person-fill': '\u{E1C3}', + 'person-circle': '\u{E05E}', + 'person-square': '\u{E129}', + 'person-rectangle': '\u{E12A}', + phone: '\u{E063}', + 'phone-fill': '\u{E064}', + photo: '\u{E1C8}', + 'photo-slash': '\u{E1C9}', + piechart: '\u{E0F1}', + 'piechart-fill': '\u{E1CA}', + pin: '\u{E12E}', + 'pin-fill': '\u{E1CB}', + 'pin-slash': '\u{E12F}', + 'pin-slash-fill': '\u{E1CC}', + play: '\u{E067}', + 'play-circle': '\u{E068}', + 'play-square': '\u{E069}', + 'play-fill': '\u{E1CD}', + 'play-circle-fill': '\u{E1CE}', + 'play-square-fill': '\u{E1CF}', + plus: '\u{E1D1}', + 'plus-circle': '\u{E1D2}', + 'plus-square': '\u{E06C}', + 'plus-circle-fill': '\u{E1D3}', + 'plus-square-fill': '\u{E1D4}', + poll: '\u{E082}', + 'poll-fill': '\u{E083}', + qrcode: '\u{E0C2}', + raisehand: '\u{E07E}', + 'raisehand-fill': '\u{E084}', + receipt: '\u{E135}', + 'receipt-rtl': '\u{E136}', + recent: '\u{E0C3}', + rectangle: '\u{E162}', + 'rectangle-fill': '\u{E1D6}', + redo: '\u{E0C6}', + refresh: '\u{E0C4}', + reply: '\u{E06D}', + 'reply-fill': '\u{E06E}', + rotate: '\u{E137}', + scan: '\u{E138}', + scribble: '\u{E0F7}', + search: '\u{E0C7}', + send: '\u{E20B}', + 'send-fill': '\u{E0C9}', + settings: '\u{E0CA}', + 'settings-fill': '\u{E0CB}', + 'shield-check': '\u{E06F}', + 'shield-check-fill': '\u{E1D8}', + square: '\u{E161}', + 'square-fill': '\u{E1FB}', + star: '\u{E0AF}', + 'star-fill': '\u{E0B0}', + sticker: '\u{E070}', + stories: '\u{E0D0}', + 'stories-fill': '\u{E0D1}', + sun: '\u{E0D2}', + 'sun-fill': '\u{E1DC}', + 'sun-horizon': '\u{E0D3}', + 'sun-horizon-fill': '\u{E1DD}', + swap: '\u{E0D4}', + thread: '\u{E071}', + 'thread-fill': '\u{E072}', + ticks: '\u{E044}', + timer: '\u{E073}', + 'timer-fill': '\u{E1E0}', + 'timer-slash': '\u{E074}', + 'timer-slash-fill': '\u{E1E1}', + trash: '\u{E0D5}', + 'trash-fill': '\u{E0D6}', + tune: '\u{E15A}', + undo: '\u{E0C5}', + upload: '\u{E0D7}', + video: '\u{E1E2}', + 'video-slash': '\u{E1E3}', + 'video-fill': '\u{E1E4}', + 'video-slash-fill': '\u{E1E6}', + videocamera: '\u{E1E8}', + 'videocamera-fill': '\u{E1EA}', + 'videocamera-slash': '\u{E1E9}', + 'videocamera-slash-fill': '\u{E1EB}', + viewonce: '\u{E078}', + 'viewonce-dash': '\u{E079}', + wifi: '\u{E15D}', + x: '\u{E1ED}', + 'x-circle': '\u{E1EE}', + 'x-circle-fill': '\u{E1F0}', + 'x-circle-dash': '\u{E15F}', + 'x-square': '\u{E1EF}', + 'x-square-fill': '\u{E1F1}', +} satisfies Record; + +const InlineDefs: Record = { + album: '\u{E001}', + 'arrow-rectangle-up': '\u{E171}', + 'arrow-rectangle-up-fill': '\u{E174}', + aspectratio: '\u{E175}', + 'aspectratio-fill': '\u{E177}', + 'audio-rectangle': '\u{E01D}', + 'audio-rectangle-fill': '\u{E17A}', + 'check-double-circle': '\u{E046}', + 'check-double-circle-fill': '\u{E047}', + 'gif-rectangle': '\u{E097}', + 'gif-rectangle-fill': '\u{E098}', + 'grid-rectangle': '\u{E197}', + 'grid-rectangle-fill': '\u{E19A}', + group: '\u{E038}', + 'group-fill': '\u{E19E}', + 'group-x': '\u{E0AE}', + 'group-x-fill': '\u{E1A0}', + invite: '\u{E1A8}', + leave: '\u{E03C}', + 'leave-rtl': '\u{E03D}', + 'person-check': '\u{E05F}', + 'person-plus': '\u{E061}', + 'person-minus': '\u{E062}', + 'person-x': '\u{E060}', + 'person-question': '\u{E06A}', + 'person-rectangle': '\u{E1C2}', + photo: '\u{E065}', + 'photo-slash': '\u{E066}', + rectangle: '\u{E1D5}', + 'rectangle-fill': '\u{E1D7}', + video: '\u{E088}', + 'video-slash': '\u{E089}', + 'video-fill': '\u{E1E5}', + 'video-slash-fill': '\u{E1E7}', + videocamera: '\u{E075}', + 'videocamera-fill': '\u{E077}', + 'videocamera-slash': '\u{E076}', + 'videocamera-slash-fill': '\u{E1EC}', +} satisfies Record; + +export function _getAllAxoSymbolIconNames(): ReadonlyArray { + return Object.keys(IconDefs) as Array; +} + +export function _getAllAxoSymbolInlineGlyphNames(): ReadonlyArray { + return Object.keys(IconDefs) as Array; +} + +export function getAxoSymbolIcon( + name: AxoSymbolIconName, + dir: 'ltr' | 'rtl' +): string { + const value = IconDefs[name]; + if (value == null) { + throw new TypeError(`Invalid symbol name for icon: ${name}`); + } + return typeof value === 'string' ? value : value[dir]; +} + +export function getAxoSymbolInlineGlyph( + name: AxoSymbolInlineGlyphName, + dir: 'ltr' | 'rtl' +): string { + const value = InlineDefs[name] ?? IconDefs[name]; + if (value == null) { + throw new TypeError(`Invalid symbol name for inline glyph: ${name}`); + } + return typeof value === 'string' ? value : value[dir]; +}