@aglyn/shared-ui-jsx-forms 1.0.0-beta.163 → 1.0.0-beta.164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/shared-ui-jsx-forms",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.164",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-color-picker": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
28
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.164",
|
|
29
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.164",
|
|
30
|
+
"@aglyn/shared-ui-color-picker": "1.0.0-beta.164",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.164",
|
|
32
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.164",
|
|
33
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.164",
|
|
34
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.164",
|
|
35
35
|
"@data-driven-forms/common": "^4.2.0",
|
|
36
36
|
"@data-driven-forms/react-form-renderer": "^4.2.0",
|
|
37
37
|
"@swc/helpers": "0.5.23",
|
|
@@ -23,7 +23,7 @@ import { GridList } from "@aglyn/shared-ui-jsx/components/grid-list";
|
|
|
23
23
|
import { generateComponentClassKeys, styled } from "@aglyn/shared-ui-theme";
|
|
24
24
|
import { useDebouncedCallback } from "@aglyn/shared-util-vendor/use-debounce";
|
|
25
25
|
import { Box, Button, ButtonBase, Collapse, Grid, Link as MuiLink, TextField as MuiTextField, Tooltip, Typography } from "@mui/material";
|
|
26
|
-
import { forwardRef, useCallback, useEffect, useMemo, useState } from "react";
|
|
26
|
+
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
27
27
|
import FormFieldGrid, { buildFieldClear } from "../mapper/form-field-grid.js";
|
|
28
28
|
import { validationMessage } from "../utils/validation-message.js";
|
|
29
29
|
import { useFieldApi } from "../vendor/data-driven-forms.js";
|
|
@@ -31,6 +31,17 @@ const iconUnset = _extends({}, DEFAULT_ICON, {
|
|
|
31
31
|
id: null,
|
|
32
32
|
name: '(none)'
|
|
33
33
|
});
|
|
34
|
+
/**
|
|
35
|
+
* What the element carries when the catalog has no icon under that id — a
|
|
36
|
+
* different thing from carrying nothing, and it has to READ differently
|
|
37
|
+
* (AGL-3266). The bundled set is one MDI release, so an id minted against a
|
|
38
|
+
* newer one (`creation-outline`, on aglyn.com's Aglyn AI card) resolves to
|
|
39
|
+
* nothing here. Naming it `(none)` hid that the element had an icon at all,
|
|
40
|
+
* and every pick then replaced a working icon the author could not see.
|
|
41
|
+
*/ const iconUnknown = (id)=>_extends({}, DEFAULT_ICON, {
|
|
42
|
+
id,
|
|
43
|
+
name: id
|
|
44
|
+
});
|
|
34
45
|
const classKeys = generateComponentClassKeys('AglynIconSelect', [
|
|
35
46
|
'root',
|
|
36
47
|
'button',
|
|
@@ -113,19 +124,45 @@ const GridListWrapper = styled('div', {
|
|
|
113
124
|
const visibleIcons = useMemo(()=>icons.length > MAX_RENDERED_ICONS ? icons.slice(0, MAX_RENDERED_ICONS) : icons, [
|
|
114
125
|
icons
|
|
115
126
|
]);
|
|
127
|
+
/**
|
|
128
|
+
* A new result set starts at its own top (AGL-3266).
|
|
129
|
+
*
|
|
130
|
+
* The grid is virtualized and keeps its scroll offset when `items`
|
|
131
|
+
* changes, so once the author had scrolled, every later search painted
|
|
132
|
+
* the MIDDLE of the new results. Searching `bullhorn` while scrolled
|
|
133
|
+
* showed Cellphone Play, Cellphone Settings… and the four Bullhorn
|
|
134
|
+
* icons — ranked first, correctly — sat off-screen above. It reads
|
|
135
|
+
* exactly like a search that ignores the query, which is how it was
|
|
136
|
+
* reported.
|
|
137
|
+
*/ const gridRef = useRef(null);
|
|
138
|
+
useEffect(()=>{
|
|
139
|
+
var _gridRef_current_scrollToIndex, _gridRef_current;
|
|
140
|
+
(_gridRef_current = gridRef.current) == null ? void 0 : (_gridRef_current_scrollToIndex = _gridRef_current.scrollToIndex) == null ? void 0 : _gridRef_current_scrollToIndex.call(_gridRef_current, {
|
|
141
|
+
index: 0
|
|
142
|
+
});
|
|
143
|
+
}, [
|
|
144
|
+
visibleIcons
|
|
145
|
+
]);
|
|
116
146
|
const [currentIcon, selectedIcon] = useMemo(()=>{
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
const resolve = (id)=>{
|
|
148
|
+
var _allIcons_find;
|
|
149
|
+
if (!id) return iconUnset;
|
|
150
|
+
// The catalog arrives async, so "not found yet" is not "not in the
|
|
151
|
+
// set" — keep saying `(none)` until it has loaded, or the opener
|
|
152
|
+
// flashes the raw id on every mount.
|
|
153
|
+
if (allIcons.length === 0) return iconUnset;
|
|
154
|
+
return (_allIcons_find = allIcons.find((icon)=>icon.id === id)) != null ? _allIcons_find : iconUnknown(id);
|
|
155
|
+
};
|
|
120
156
|
return [
|
|
121
|
-
|
|
122
|
-
|
|
157
|
+
resolve(currentValue),
|
|
158
|
+
resolve(selected)
|
|
123
159
|
];
|
|
124
160
|
}, [
|
|
125
161
|
currentValue,
|
|
126
162
|
selected,
|
|
127
163
|
allIcons
|
|
128
164
|
]);
|
|
165
|
+
/** True once the catalog is in and still has nothing under that id. */ const currentIsUnknown = Boolean(currentValue) && currentIcon.id === currentValue && !allIcons.some((icon)=>icon.id === currentValue);
|
|
129
166
|
const handleButtonClick = useCallback(()=>{
|
|
130
167
|
setOpen((prev)=>!prev);
|
|
131
168
|
}, [
|
|
@@ -323,7 +360,14 @@ const GridListWrapper = styled('div', {
|
|
|
323
360
|
variant: "caption",
|
|
324
361
|
color: "text.secondary",
|
|
325
362
|
children: hasPendingPick ? `Not applied yet — press Choose to replace ${currentIcon.name}.` : 'Pick an icon, then press Choose to apply it.'
|
|
326
|
-
})
|
|
363
|
+
}),
|
|
364
|
+
currentIsUnknown ? /*#__PURE__*/ _jsx(Typography, {
|
|
365
|
+
"data-aglyn-icon-unknown": "",
|
|
366
|
+
component: "div",
|
|
367
|
+
variant: "caption",
|
|
368
|
+
color: "text.secondary",
|
|
369
|
+
children: `This element carries ${currentValue}, which is not in the icon library — it renders on the site but has no preview here. Choosing another icon replaces it.`
|
|
370
|
+
}) : null
|
|
327
371
|
]
|
|
328
372
|
}),
|
|
329
373
|
/*#__PURE__*/ _jsx(Grid, {
|
|
@@ -347,6 +391,7 @@ const GridListWrapper = styled('div', {
|
|
|
347
391
|
/*#__PURE__*/ _jsxs(GridListWrapper, {
|
|
348
392
|
children: [
|
|
349
393
|
/*#__PURE__*/ _jsx(GridList, _extends({
|
|
394
|
+
ref: gridRef,
|
|
350
395
|
GridContainerProps: {
|
|
351
396
|
spacing: 1
|
|
352
397
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx-forms/src/lib/components/icon-select.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DEFAULT_ICON,\n type Icon,\n mdiCheck,\n mdiChevronDown,\n mdiChevronUp,\n} from '@aglyn/shared-data-mdi'\nimport { CardListItem, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useMdiIconsFuzzy } from '@aglyn/shared-ui-jsx/hooks/mdi-icon/use-mdi-icons-fuzzy'\nimport { GridList } from '@aglyn/shared-ui-jsx/components/grid-list'\nimport { generateComponentClassKeys, styled } from '@aglyn/shared-ui-theme'\nimport { useDebouncedCallback } from '@aglyn/shared-util-vendor/use-debounce'\nimport {\n Box,\n Button,\n ButtonBase,\n Collapse,\n Grid,\n Link as MuiLink,\n TextField as MuiTextField,\n Tooltip,\n Typography,\n} from '@mui/material'\n\nimport {\n forwardRef,\n type HTMLAttributes,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react'\nimport FormFieldGrid, { buildFieldClear } from '../mapper/form-field-grid'\nimport { validationMessage } from '../utils/validation-message'\n\nimport {\n useFieldApi,\n type UseFieldApiConfig,\n} from '../vendor/data-driven-forms'\n\nconst iconUnset = { ...DEFAULT_ICON, id: null, name: '(none)' }\nconst classKeys = generateComponentClassKeys('AglynIconSelect', [\n 'root',\n 'button',\n 'icon',\n 'label',\n 'selected',\n 'opener',\n 'preview',\n 'collapse',\n 'collapseInner',\n 'gridListWrapper',\n 'gridList',\n])\n\nconst StyledCollapse = styled(Collapse, {\n name: 'AglynIconSelectCollapse',\n})(({ theme }) => ({\n '& .MuiCollapse-wrapper': {\n height: 412,\n },\n '& .MuiCollapse-wrapperInner': {\n paddingTop: theme.spacing(1),\n display: 'flex',\n flexDirection: 'column',\n flexGrow: 1,\n flexShrink: 0,\n alignSelf: 'stretch',\n },\n}))\n\nconst GridListWrapper = styled('div', {\n name: 'AglynIconSelectListWrapper',\n})(({ theme }) => {\n const tv = (theme as any).vars || theme\n return {\n height: '100%',\n marginTop: theme.spacing(1),\n border: `1px solid ${tv.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n [`& .${classKeys.gridList}`]: {\n padding: theme.spacing(1),\n },\n }\n})\n\nexport interface IconSelectProps extends HTMLAttributes<HTMLDivElement> {\n initialValue?: string\n classes?: Partial<typeof classKeys>\n}\n\nexport interface IconSelectControlProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Picked icon id, or empty for \"nothing chosen yet\". */\n value?: string\n onChange?: (iconId: string) => void\n label?: JSX.Node\n helperText?: JSX.Node\n GridListProps?: Record<string, unknown>\n classes?: Partial<typeof classKeys>\n}\n\n/**\n * The picker itself, with no form library attached (AGL-1193).\n *\n * Extracted so surfaces outside the designer can offer the SAME picker\n * rather than growing a second one: the console sets a reusable\n * component's icon from a plain dialog and a plain detail form, neither of\n * which runs inside a data-driven-forms tree. The field below is now a\n * thin adapter over this, so the two can't drift.\n */\nexport const IconSelectControl = forwardRef<any, IconSelectControlProps>(\n (props, ref) => {\n const {\n value: currentValue = '',\n onChange,\n label,\n helperText: helperContent,\n GridListProps,\n ...rest\n } = props\n const [open, setOpen] = useState(false)\n const [icons, allIcons, applyFilter, clearFilter] = useMdiIconsFuzzy()\n /**\n * The DRAFT pick (AGL-2486). Picking is two steps: clicking an icon only\n * moves this, and `Choose` is the one thing that reaches `onChange` and\n * therefore the canvas.\n */\n const [selected, setSelected] = useState(currentValue)\n /**\n * Re-seeded from the live value whenever the panel opens or the value\n * changes underneath. Without this an abandoned pick survived the panel\n * being closed, so the NEXT `Choose` applied an icon the author had\n * already walked away from.\n */\n useEffect(() => {\n setSelected(currentValue)\n }, [currentValue, open])\n\n // Rendering all ~6,600 icons at once froze the panel (AGL-340): cap the\n // grid and let search narrow the rest.\n const MAX_RENDERED_ICONS = 240\n const visibleIcons = useMemo(\n () =>\n icons.length > MAX_RENDERED_ICONS\n ? icons.slice(0, MAX_RENDERED_ICONS)\n : icons,\n [icons],\n )\n\n const [currentIcon, selectedIcon] = useMemo(() => {\n const findIcon = (id: string) => allIcons.find((icon) => icon.id === id)\n const currentIcon = (currentValue && findIcon(currentValue)) || iconUnset\n const selectedIcon = (selected && findIcon(selected)) || iconUnset\n return [currentIcon, selectedIcon]\n }, [currentValue, selected, allIcons])\n\n const handleButtonClick = useCallback(() => {\n setOpen((prev) => !prev)\n }, [setOpen])\n /** Nothing to confirm until the draft differs from what is applied. */\n const hasPendingPick = selected !== currentValue\n const handleCancelButtonClick = useCallback(() => {\n // Closing re-seeds the draft (the effect above), so this only has to\n // close — but it says so explicitly, because \"cancel leaves the canvas\n // alone\" is the guarantee, not an emergent property of an effect.\n setSelected(currentValue)\n setOpen(false)\n }, [currentValue])\n const handleChooseButtonClick = useCallback(() => {\n if (selected === currentValue) return\n onChange?.(selected)\n setOpen(false)\n }, [onChange, selected, currentValue])\n const updateFilter = useDebouncedCallback((value?: string) => {\n if (value) {\n applyFilter(value)\n } else {\n clearFilter()\n }\n }, 300)\n const handleFilterChange = useCallback(\n (e) => {\n const target = e.currentTarget\n if (target && target.value) {\n updateFilter(target.value)\n } else {\n updateFilter()\n }\n },\n [updateFilter],\n )\n const handleItemClick = useCallback(\n (e, item: Icon) => {\n setSelected(item.id)\n },\n [setSelected],\n )\n\n const renderItemContent = useCallback(\n function RenderItemContent(item: Icon, key: number) {\n const isSelected = Boolean(selected) && selected === item.id\n return (\n <Tooltip\n key={item.id ?? key}\n title={item.name}\n disableInteractive\n enterDelay={375}\n enterNextDelay={745}\n >\n <CardListItem\n item={item}\n selected={isSelected}\n // A real toggle, so the pick is announced and not only\n // painted (AGL-2486).\n role=\"button\"\n aria-pressed={isSelected}\n onClick={(e) => handleItemClick(e, item)}\n >\n <div>\n <MdiIcon fontSize=\"medium\" path={item.path} />\n </div>\n {isSelected ? (\n // Belt and braces on the fill: a tick reads as \"picked\"\n // even where the tint is subtle, and gives the state\n // something to assert against.\n <Box\n data-aglyn-icon-selected=\"\"\n sx={{\n position: 'absolute',\n top: 2,\n right: 2,\n lineHeight: 0,\n fontSize: 14,\n borderRadius: '50%',\n bgcolor: 'primary.contrastText',\n color: 'primary.main',\n }}\n >\n <MdiIcon fontSize=\"inherit\" path={mdiCheck.path} />\n </Box>\n ) : null}\n </CardListItem>\n </Tooltip>\n )\n },\n [selected, handleItemClick],\n )\n\n return (\n <Grid ref={ref} container>\n <Grid spacing={2} container sx={{\n alignItems: 'center'\n }}>\n <Grid>\n <ButtonBase\n onClick={handleButtonClick}\n disableRipple\n sx={(theme) => ({ fontSize: theme.typography.pxToRem(38) })}\n title={currentIcon.name || 'Choose icon'}\n >\n <MdiIcon fontSize=\"inherit\" path={currentIcon.path} />\n </ButtonBase>\n </Grid>\n <Grid\n size={{\n sm: \"grow\"\n }}>\n <Typography component=\"div\" variant=\"caption\">\n {label}\n </Typography>\n <MuiLink\n onClick={handleButtonClick}\n variant=\"button\"\n component=\"button\"\n color=\"primary\"\n sx={{\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'flex-start',\n }}\n >\n <span>\n <MdiIcon\n fontSize=\"inherit\"\n path={open ? mdiChevronUp.path : mdiChevronDown.path}\n />\n </span>\n <span>{currentIcon.name}</span>\n </MuiLink>\n </Grid>\n </Grid>\n <Grid size={12}>\n <StyledCollapse in={open}>\n <Grid spacing={2} container sx={{\n alignItems: \"center\"\n }}>\n <Grid size={12}>\n <MuiTextField\n onChange={handleFilterChange}\n placeholder=\"Search icons...\"\n size=\"small\"\n helperText={helperContent}\n fullWidth\n />\n </Grid>\n <Grid size=\"grow\">\n <Typography component=\"div\" variant=\"body2\">\n Selected icon: <b>{selectedIcon.name}</b>\n </Typography>\n <Typography\n component=\"div\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {hasPendingPick\n ? `Not applied yet — press Choose to replace ${currentIcon.name}.`\n : 'Pick an icon, then press Choose to apply it.'}\n </Typography>\n </Grid>\n <Grid>\n <Button color=\"inherit\" onClick={handleCancelButtonClick}>\n Cancel\n </Button>\n </Grid>\n <Grid>\n <Button\n color=\"primary\"\n disabled={!hasPendingPick}\n onClick={handleChooseButtonClick}\n variant=\"contained\"\n >\n Choose\n </Button>\n </Grid>\n </Grid>\n\n <GridListWrapper>\n <GridList\n GridContainerProps={{ spacing: 1 }}\n GridItemProps={{ size: { xs: 2 } }}\n ListWrapperProps={{ className: classKeys.gridList }}\n items={visibleIcons}\n renderItemContent={renderItemContent}\n {...GridListProps}\n />\n {icons.length > visibleIcons.length ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', px: 1, pb: 1 }}\n >\n {`Showing ${visibleIcons.length} of ${icons.length} icons — search to narrow down.`}\n </Typography>\n ) : null}\n {allIcons.length === 0 ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', px: 1, pb: 1 }}\n >\n {'Loading icon catalog…'}\n </Typography>\n ) : null}\n </GridListWrapper>\n </StyledCollapse>\n </Grid>\n </Grid>\n )\n },\n)\nIconSelectControl.displayName = 'IconSelectControl'\n\n/**\n * The data-driven-forms field: everything form-shaped (validation message,\n * helper/description precedence, the final-form input) resolved here, and\n * the picking itself delegated to {@link IconSelectControl}.\n *\n * Sits in the same {@link FormFieldGrid} every other field does, so the corner\n * controls a schema asks for — the help tip, the opt-in clear, a caller's own\n * — are where they are on every other field rather than silently dropped.\n */\nconst IconSelectComponent = forwardRef<any, IconSelectProps>((props, ref) => {\n const {\n input,\n isReadOnly,\n isDisabled,\n placeholder,\n isRequired,\n label,\n helperText,\n description,\n validateOnMount,\n meta,\n inputProps,\n GridListProps,\n help,\n clearable,\n FormFieldGridProps = {},\n ...rest\n } = useFieldApi(props as UseFieldApiConfig)\n\n const invalidMessage = validationMessage(meta, validateOnMount)\n const helperContent = [\n invalidMessage,\n (meta.touched || validateOnMount) && meta.warning,\n helperText,\n description,\n ].find((i) => Boolean(i))\n\n // The picker can only ever choose an icon, so without this nothing takes\n // one back off again.\n const clear = buildFieldClear({\n clearable,\n label,\n hasValue: Boolean(input.value),\n locked: Boolean(isDisabled || isReadOnly),\n onClear: () => input.onChange(''),\n })\n\n return (\n <FormFieldGrid help={help} clear={clear} {...FormFieldGridProps}>\n <IconSelectControl\n ref={ref}\n value={input.value}\n onChange={input.onChange}\n label={label}\n helperText={helperContent}\n GridListProps={GridListProps}\n />\n </FormFieldGrid>\n )\n})\n\nIconSelectComponent.displayName = 'IconSelectComponent'\nIconSelectComponent.aglyn = true\n\nexport default IconSelectComponent\n"],"names":["DEFAULT_ICON","mdiCheck","mdiChevronDown","mdiChevronUp","CardListItem","MdiIcon","useMdiIconsFuzzy","GridList","generateComponentClassKeys","styled","useDebouncedCallback","Box","Button","ButtonBase","Collapse","Grid","Link","MuiLink","TextField","MuiTextField","Tooltip","Typography","forwardRef","useCallback","useEffect","useMemo","useState","FormFieldGrid","buildFieldClear","validationMessage","useFieldApi","iconUnset","id","name","classKeys","StyledCollapse","theme","height","paddingTop","spacing","display","flexDirection","flexGrow","flexShrink","alignSelf","GridListWrapper","tv","vars","marginTop","border","palette","divider","borderRadius","shape","gridList","padding","IconSelectControl","props","ref","value","currentValue","onChange","label","helperText","helperContent","GridListProps","rest","open","setOpen","icons","allIcons","applyFilter","clearFilter","selected","setSelected","MAX_RENDERED_ICONS","visibleIcons","length","slice","currentIcon","selectedIcon","findIcon","find","icon","handleButtonClick","prev","hasPendingPick","handleCancelButtonClick","handleChooseButtonClick","updateFilter","handleFilterChange","e","target","currentTarget","handleItemClick","item","renderItemContent","RenderItemContent","key","isSelected","Boolean","title","disableInteractive","enterDelay","enterNextDelay","role","aria-pressed","onClick","div","fontSize","path","data-aglyn-icon-selected","sx","position","top","right","lineHeight","bgcolor","color","container","alignItems","disableRipple","typography","pxToRem","size","sm","component","variant","justifyContent","span","in","placeholder","fullWidth","b","disabled","GridContainerProps","GridItemProps","xs","ListWrapperProps","className","items","px","pb","displayName","IconSelectComponent","input","isReadOnly","isDisabled","isRequired","description","validateOnMount","meta","inputProps","help","clearable","FormFieldGridProps","invalidMessage","touched","warning","i","clear","hasValue","locked","onClear","aglyn"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,YAAY,EAEZC,QAAQ,EACRC,cAAc,EACdC,YAAY,QACP,yBAAwB;AAC/B,SAASC,YAAY,EAAEC,OAAO,QAAQ,uBAAsB;AAC5D,SAASC,gBAAgB,QAAQ,0DAAyD;AAC1F,SAASC,QAAQ,QAAQ,4CAA2C;AACpE,SAASC,0BAA0B,EAAEC,MAAM,QAAQ,yBAAwB;AAC3E,SAASC,oBAAoB,QAAQ,yCAAwC;AAC7E,SACEC,GAAG,EACHC,MAAM,EACNC,UAAU,EACVC,QAAQ,EACRC,IAAI,EACJC,QAAQC,OAAO,EACfC,aAAaC,YAAY,EACzBC,OAAO,EACPC,UAAU,QACL,gBAAe;AAEtB,SACEC,UAAU,EAEVC,WAAW,EACXC,SAAS,EACTC,OAAO,EACPC,QAAQ,QACH,QAAO;AACd,OAAOC,iBAAiBC,eAAe,QAAQ,+BAA2B;AAC1E,SAASC,iBAAiB,QAAQ,iCAA6B;AAE/D,SACEC,WAAW,QAEN,iCAA6B;AAEpC,MAAMC,YAAY,aAAK/B;IAAcgC,IAAI;IAAMC,MAAM;;AACrD,MAAMC,YAAY1B,2BAA2B,mBAAmB;IAC9D;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAM2B,iBAAiB1B,OAAOK,UAAU;IACtCmB,MAAM;AACR,GAAG,CAAC,EAAEG,KAAK,EAAE,GAAM,CAAA;QACjB,0BAA0B;YACxBC,QAAQ;QACV;QACA,+BAA+B;YAC7BC,YAAYF,MAAMG,OAAO,CAAC;YAC1BC,SAAS;YACTC,eAAe;YACfC,UAAU;YACVC,YAAY;YACZC,WAAW;QACb;IACF,CAAA;AAEA,MAAMC,kBAAkBpC,OAAO,OAAO;IACpCwB,MAAM;AACR,GAAG,CAAC,EAAEG,KAAK,EAAE;IACX,MAAMU,KAAK,AAACV,MAAcW,IAAI,IAAIX;IAClC,OAAO;QACLC,QAAQ;QACRW,WAAWZ,MAAMG,OAAO,CAAC;QACzBU,QAAQ,CAAC,UAAU,EAAEH,GAAGI,OAAO,CAACC,OAAO,EAAE;QACzCC,cAAchB,MAAMiB,KAAK,CAACD,YAAY;QACtC,CAAC,CAAC,GAAG,EAAElB,UAAUoB,QAAQ,EAAE,CAAC,EAAE;YAC5BC,SAASnB,MAAMG,OAAO,CAAC;QACzB;IACF;AACF;AAkBA;;;;;;;;CAQC,GACD,OAAO,MAAMiB,kCAAoBlC,WAC/B,CAACmC,OAAOC;IACN,MAAM,EACJC,OAAOC,eAAe,EAAE,EACxBC,QAAQ,EACRC,KAAK,EACLC,YAAYC,aAAa,EACzBC,aAAa,EAEd,GAAGR,OADCS,wCACDT;;;;;;;IACJ,MAAM,CAACU,MAAMC,QAAQ,GAAG1C,SAAS;IACjC,MAAM,CAAC2C,OAAOC,UAAUC,aAAaC,YAAY,GAAGlE;IACpD;;;;KAIC,GACD,MAAM,CAACmE,UAAUC,YAAY,GAAGhD,SAASkC;IACzC;;;;;KAKC,GACDpC,UAAU;QACRkD,YAAYd;IACd,GAAG;QAACA;QAAcO;KAAK;IAEvB,wEAAwE;IACxE,uCAAuC;IACvC,MAAMQ,qBAAqB;IAC3B,MAAMC,eAAenD,QACnB,IACE4C,MAAMQ,MAAM,GAAGF,qBACXN,MAAMS,KAAK,CAAC,GAAGH,sBACfN,OACN;QAACA;KAAM;IAGT,MAAM,CAACU,aAAaC,aAAa,GAAGvD,QAAQ;QAC1C,MAAMwD,WAAW,CAACjD,KAAesC,SAASY,IAAI,CAAC,CAACC,OAASA,KAAKnD,EAAE,KAAKA;QACrE,MAAM+C,cAAc,AAACnB,gBAAgBqB,SAASrB,iBAAkB7B;QAChE,MAAMiD,eAAe,AAACP,YAAYQ,SAASR,aAAc1C;QACzD,OAAO;YAACgD;YAAaC;SAAa;IACpC,GAAG;QAACpB;QAAca;QAAUH;KAAS;IAErC,MAAMc,oBAAoB7D,YAAY;QACpC6C,QAAQ,CAACiB,OAAS,CAACA;IACrB,GAAG;QAACjB;KAAQ;IACZ,qEAAqE,GACrE,MAAMkB,iBAAiBb,aAAab;IACpC,MAAM2B,0BAA0BhE,YAAY;QAC1C,qEAAqE;QACrE,uEAAuE;QACvE,kEAAkE;QAClEmD,YAAYd;QACZQ,QAAQ;IACV,GAAG;QAACR;KAAa;IACjB,MAAM4B,0BAA0BjE,YAAY;QAC1C,IAAIkD,aAAab,cAAc;QAC/BC,4BAAAA,SAAWY;QACXL,QAAQ;IACV,GAAG;QAACP;QAAUY;QAAUb;KAAa;IACrC,MAAM6B,eAAe/E,qBAAqB,CAACiD;QACzC,IAAIA,OAAO;YACTY,YAAYZ;QACd,OAAO;YACLa;QACF;IACF,GAAG;IACH,MAAMkB,qBAAqBnE,YACzB,CAACoE;QACC,MAAMC,SAASD,EAAEE,aAAa;QAC9B,IAAID,UAAUA,OAAOjC,KAAK,EAAE;YAC1B8B,aAAaG,OAAOjC,KAAK;QAC3B,OAAO;YACL8B;QACF;IACF,GACA;QAACA;KAAa;IAEhB,MAAMK,kBAAkBvE,YACtB,CAACoE,GAAGI;QACFrB,YAAYqB,KAAK/D,EAAE;IACrB,GACA;QAAC0C;KAAY;IAGf,MAAMsB,oBAAoBzE,YACxB,SAAS0E,kBAAkBF,IAAU,EAAEG,GAAW;YAIvCH;QAHT,MAAMI,aAAaC,QAAQ3B,aAAaA,aAAasB,KAAK/D,EAAE;QAC5D,qBACE,KAACZ;YAECiF,OAAON,KAAK9D,IAAI;YAChBqE,kBAAkB;YAClBC,YAAY;YACZC,gBAAgB;sBAEhB,cAAA,MAACpG;gBACC2F,MAAMA;gBACNtB,UAAU0B;gBACV,uDAAuD;gBACvD,sBAAsB;gBACtBM,MAAK;gBACLC,gBAAcP;gBACdQ,SAAS,CAAChB,IAAMG,gBAAgBH,GAAGI;;kCAEnC,KAACa;kCACC,cAAA,KAACvG;4BAAQwG,UAAS;4BAASC,MAAMf,KAAKe,IAAI;;;oBAE3CX,aACC,wDAAwD;oBACxD,qDAAqD;oBACrD,+BAA+B;kCAC/B,KAACxF;wBACCoG,4BAAyB;wBACzBC,IAAI;4BACFC,UAAU;4BACVC,KAAK;4BACLC,OAAO;4BACPC,YAAY;4BACZP,UAAU;4BACVzD,cAAc;4BACdiE,SAAS;4BACTC,OAAO;wBACT;kCAEA,cAAA,KAACjH;4BAAQwG,UAAS;4BAAUC,MAAM7G,SAAS6G,IAAI;;yBAE/C;;;YArCDf,WAAAA,KAAK/D,EAAE,YAAP+D,WAAWG;IAyCtB,GACA;QAACzB;QAAUqB;KAAgB;IAG7B,qBACE,MAAC/E;QAAK2C,KAAKA;QAAK6D,SAAS;;0BACvB,MAACxG;gBAAKwB,SAAS;gBAAGgF,SAAS;gBAACP,IAAI;oBAC9BQ,YAAY;gBACd;;kCACE,KAACzG;kCACC,cAAA,KAACF;4BACC8F,SAASvB;4BACTqC,aAAa;4BACbT,IAAI,CAAC5E,QAAW,CAAA;oCAAEyE,UAAUzE,MAAMsF,UAAU,CAACC,OAAO,CAAC;gCAAI,CAAA;4BACzDtB,OAAOtB,YAAY9C,IAAI,IAAI;sCAE3B,cAAA,KAAC5B;gCAAQwG,UAAS;gCAAUC,MAAM/B,YAAY+B,IAAI;;;;kCAGtD,MAAC/F;wBACC6G,MAAM;4BACJC,IAAI;wBACN;;0CACA,KAACxG;gCAAWyG,WAAU;gCAAMC,SAAQ;0CACjCjE;;0CAEH,MAAC7C;gCACC0F,SAASvB;gCACT2C,SAAQ;gCACRD,WAAU;gCACVR,OAAM;gCACNN,IAAI;oCACFxE,SAAS;oCACTC,eAAe;oCACfuF,gBAAgB;gCAClB;;kDAEA,KAACC;kDACC,cAAA,KAAC5H;4CACCwG,UAAS;4CACTC,MAAM3C,OAAOhE,aAAa2G,IAAI,GAAG5G,eAAe4G,IAAI;;;kDAGxD,KAACmB;kDAAMlD,YAAY9C,IAAI;;;;;;;;0BAI7B,KAAClB;gBAAK6G,MAAM;0BACV,cAAA,MAACzF;oBAAe+F,IAAI/D;;sCAClB,MAACpD;4BAAKwB,SAAS;4BAAGgF,SAAS;4BAACP,IAAI;gCAC9BQ,YAAY;4BACd;;8CACE,KAACzG;oCAAK6G,MAAM;8CACV,cAAA,KAACzG;wCACC0C,UAAU6B;wCACVyC,aAAY;wCACZP,MAAK;wCACL7D,YAAYC;wCACZoE,SAAS;;;8CAGb,MAACrH;oCAAK6G,MAAK;;sDACT,MAACvG;4CAAWyG,WAAU;4CAAMC,SAAQ;;gDAAQ;8DAC3B,KAACM;8DAAGrD,aAAa/C,IAAI;;;;sDAEtC,KAACZ;4CACCyG,WAAU;4CACVC,SAAQ;4CACRT,OAAM;sDAELhC,iBACG,CAAC,0CAA0C,EAAEP,YAAY9C,IAAI,CAAC,CAAC,CAAC,GAChE;;;;8CAGR,KAAClB;8CACC,cAAA,KAACH;wCAAO0G,OAAM;wCAAUX,SAASpB;kDAAyB;;;8CAI5D,KAACxE;8CACC,cAAA,KAACH;wCACC0G,OAAM;wCACNgB,UAAU,CAAChD;wCACXqB,SAASnB;wCACTuC,SAAQ;kDACT;;;;;sCAML,MAAClF;;8CACC,KAACtC;oCACCgI,oBAAoB;wCAAEhG,SAAS;oCAAE;oCACjCiG,eAAe;wCAAEZ,MAAM;4CAAEa,IAAI;wCAAE;oCAAE;oCACjCC,kBAAkB;wCAAEC,WAAWzG,UAAUoB,QAAQ;oCAAC;oCAClDsF,OAAOhE;oCACPoB,mBAAmBA;mCACf/B;gCAELI,MAAMQ,MAAM,GAAGD,aAAaC,MAAM,iBACjC,KAACxD;oCACC0G,SAAQ;oCACRT,OAAM;oCACNN,IAAI;wCAAExE,SAAS;wCAASqG,IAAI;wCAAGC,IAAI;oCAAE;8CAEpC,CAAC,QAAQ,EAAElE,aAAaC,MAAM,CAAC,IAAI,EAAER,MAAMQ,MAAM,CAAC,+BAA+B,CAAC;qCAEnF;gCACHP,SAASO,MAAM,KAAK,kBACnB,KAACxD;oCACC0G,SAAQ;oCACRT,OAAM;oCACNN,IAAI;wCAAExE,SAAS;wCAASqG,IAAI;wCAAGC,IAAI;oCAAE;8CAEpC;qCAED;;;;;;;;AAMhB,GACD;AACDtF,kBAAkBuF,WAAW,GAAG;AAEhC;;;;;;;;CAQC,GACD,MAAMC,oCAAsB1H,WAAiC,CAACmC,OAAOC;IACnE,MAiBI5B,eAAAA,YAAY2B,QAjBV,EACJwF,KAAK,EACLC,UAAU,EACVC,UAAU,EACVhB,WAAW,EACXiB,UAAU,EACVtF,KAAK,EACLC,UAAU,EACVsF,WAAW,EACXC,eAAe,EACfC,IAAI,EACJC,UAAU,EACVvF,aAAa,EACbwF,IAAI,EACJC,SAAS,EACTC,qBAAqB,CAAC,CAAC,EAExB,GAAG7H,cADCoC,wCACDpC;;;;;;;;;;;;;;;;;IAEJ,MAAM8H,iBAAiB/H,kBAAkB0H,MAAMD;IAC/C,MAAMtF,gBAAgB;QACpB4F;QACCL,CAAAA,KAAKM,OAAO,IAAIP,eAAc,KAAMC,KAAKO,OAAO;QACjD/F;QACAsF;KACD,CAACnE,IAAI,CAAC,CAAC6E,IAAM3D,QAAQ2D;IAEtB,yEAAyE;IACzE,sBAAsB;IACtB,MAAMC,QAAQpI,gBAAgB;QAC5B8H;QACA5F;QACAmG,UAAU7D,QAAQ6C,MAAMtF,KAAK;QAC7BuG,QAAQ9D,QAAQ+C,cAAcD;QAC9BiB,SAAS,IAAMlB,MAAMpF,QAAQ,CAAC;IAChC;IAEA,qBACE,KAAClC;QAAc8H,MAAMA;QAAMO,OAAOA;OAAWL;kBAC3C,cAAA,KAACnG;YACCE,KAAKA;YACLC,OAAOsF,MAAMtF,KAAK;YAClBE,UAAUoF,MAAMpF,QAAQ;YACxBC,OAAOA;YACPC,YAAYC;YACZC,eAAeA;;;AAIvB;AAEA+E,oBAAoBD,WAAW,GAAG;AAClCC,oBAAoBoB,KAAK,GAAG;AAE5B,eAAepB,oBAAmB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx-forms/src/lib/components/icon-select.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DEFAULT_ICON,\n type Icon,\n mdiCheck,\n mdiChevronDown,\n mdiChevronUp,\n} from '@aglyn/shared-data-mdi'\nimport { CardListItem, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useMdiIconsFuzzy } from '@aglyn/shared-ui-jsx/hooks/mdi-icon/use-mdi-icons-fuzzy'\nimport { GridList } from '@aglyn/shared-ui-jsx/components/grid-list'\nimport { generateComponentClassKeys, styled } from '@aglyn/shared-ui-theme'\nimport { useDebouncedCallback } from '@aglyn/shared-util-vendor/use-debounce'\nimport {\n Box,\n Button,\n ButtonBase,\n Collapse,\n Grid,\n Link as MuiLink,\n TextField as MuiTextField,\n Tooltip,\n Typography,\n} from '@mui/material'\n\nimport {\n forwardRef,\n type HTMLAttributes,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport FormFieldGrid, { buildFieldClear } from '../mapper/form-field-grid'\nimport { validationMessage } from '../utils/validation-message'\n\nimport {\n useFieldApi,\n type UseFieldApiConfig,\n} from '../vendor/data-driven-forms'\n\nconst iconUnset = { ...DEFAULT_ICON, id: null, name: '(none)' }\n/**\n * What the element carries when the catalog has no icon under that id — a\n * different thing from carrying nothing, and it has to READ differently\n * (AGL-3266). The bundled set is one MDI release, so an id minted against a\n * newer one (`creation-outline`, on aglyn.com's Aglyn AI card) resolves to\n * nothing here. Naming it `(none)` hid that the element had an icon at all,\n * and every pick then replaced a working icon the author could not see.\n */\nconst iconUnknown = (id: string) => ({ ...DEFAULT_ICON, id, name: id })\n\n/** The handle {@link GridList} forwards, narrowed to what is used here. */\ntype ScrollableGrid = { scrollToIndex?: (opts: { index: number }) => void }\nconst classKeys = generateComponentClassKeys('AglynIconSelect', [\n 'root',\n 'button',\n 'icon',\n 'label',\n 'selected',\n 'opener',\n 'preview',\n 'collapse',\n 'collapseInner',\n 'gridListWrapper',\n 'gridList',\n])\n\nconst StyledCollapse = styled(Collapse, {\n name: 'AglynIconSelectCollapse',\n})(({ theme }) => ({\n '& .MuiCollapse-wrapper': {\n height: 412,\n },\n '& .MuiCollapse-wrapperInner': {\n paddingTop: theme.spacing(1),\n display: 'flex',\n flexDirection: 'column',\n flexGrow: 1,\n flexShrink: 0,\n alignSelf: 'stretch',\n },\n}))\n\nconst GridListWrapper = styled('div', {\n name: 'AglynIconSelectListWrapper',\n})(({ theme }) => {\n const tv = (theme as any).vars || theme\n return {\n height: '100%',\n marginTop: theme.spacing(1),\n border: `1px solid ${tv.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n [`& .${classKeys.gridList}`]: {\n padding: theme.spacing(1),\n },\n }\n})\n\nexport interface IconSelectProps extends HTMLAttributes<HTMLDivElement> {\n initialValue?: string\n classes?: Partial<typeof classKeys>\n}\n\nexport interface IconSelectControlProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Picked icon id, or empty for \"nothing chosen yet\". */\n value?: string\n onChange?: (iconId: string) => void\n label?: JSX.Node\n helperText?: JSX.Node\n GridListProps?: Record<string, unknown>\n classes?: Partial<typeof classKeys>\n}\n\n/**\n * The picker itself, with no form library attached (AGL-1193).\n *\n * Extracted so surfaces outside the designer can offer the SAME picker\n * rather than growing a second one: the console sets a reusable\n * component's icon from a plain dialog and a plain detail form, neither of\n * which runs inside a data-driven-forms tree. The field below is now a\n * thin adapter over this, so the two can't drift.\n */\nexport const IconSelectControl = forwardRef<any, IconSelectControlProps>(\n (props, ref) => {\n const {\n value: currentValue = '',\n onChange,\n label,\n helperText: helperContent,\n GridListProps,\n ...rest\n } = props\n const [open, setOpen] = useState(false)\n const [icons, allIcons, applyFilter, clearFilter] = useMdiIconsFuzzy()\n /**\n * The DRAFT pick (AGL-2486). Picking is two steps: clicking an icon only\n * moves this, and `Choose` is the one thing that reaches `onChange` and\n * therefore the canvas.\n */\n const [selected, setSelected] = useState(currentValue)\n /**\n * Re-seeded from the live value whenever the panel opens or the value\n * changes underneath. Without this an abandoned pick survived the panel\n * being closed, so the NEXT `Choose` applied an icon the author had\n * already walked away from.\n */\n useEffect(() => {\n setSelected(currentValue)\n }, [currentValue, open])\n\n // Rendering all ~6,600 icons at once froze the panel (AGL-340): cap the\n // grid and let search narrow the rest.\n const MAX_RENDERED_ICONS = 240\n const visibleIcons = useMemo(\n () =>\n icons.length > MAX_RENDERED_ICONS\n ? icons.slice(0, MAX_RENDERED_ICONS)\n : icons,\n [icons],\n )\n\n /**\n * A new result set starts at its own top (AGL-3266).\n *\n * The grid is virtualized and keeps its scroll offset when `items`\n * changes, so once the author had scrolled, every later search painted\n * the MIDDLE of the new results. Searching `bullhorn` while scrolled\n * showed Cellphone Play, Cellphone Settings… and the four Bullhorn\n * icons — ranked first, correctly — sat off-screen above. It reads\n * exactly like a search that ignores the query, which is how it was\n * reported.\n */\n const gridRef = useRef<ScrollableGrid>(null)\n useEffect(() => {\n gridRef.current?.scrollToIndex?.({ index: 0 })\n }, [visibleIcons])\n\n const [currentIcon, selectedIcon] = useMemo(() => {\n const resolve = (id: string) => {\n if (!id) return iconUnset\n // The catalog arrives async, so \"not found yet\" is not \"not in the\n // set\" — keep saying `(none)` until it has loaded, or the opener\n // flashes the raw id on every mount.\n if (allIcons.length === 0) return iconUnset\n return allIcons.find((icon) => icon.id === id) ?? iconUnknown(id)\n }\n return [resolve(currentValue), resolve(selected)]\n }, [currentValue, selected, allIcons])\n /** True once the catalog is in and still has nothing under that id. */\n const currentIsUnknown =\n Boolean(currentValue) &&\n currentIcon.id === currentValue &&\n !allIcons.some((icon) => icon.id === currentValue)\n\n const handleButtonClick = useCallback(() => {\n setOpen((prev) => !prev)\n }, [setOpen])\n /** Nothing to confirm until the draft differs from what is applied. */\n const hasPendingPick = selected !== currentValue\n const handleCancelButtonClick = useCallback(() => {\n // Closing re-seeds the draft (the effect above), so this only has to\n // close — but it says so explicitly, because \"cancel leaves the canvas\n // alone\" is the guarantee, not an emergent property of an effect.\n setSelected(currentValue)\n setOpen(false)\n }, [currentValue])\n const handleChooseButtonClick = useCallback(() => {\n if (selected === currentValue) return\n onChange?.(selected)\n setOpen(false)\n }, [onChange, selected, currentValue])\n const updateFilter = useDebouncedCallback((value?: string) => {\n if (value) {\n applyFilter(value)\n } else {\n clearFilter()\n }\n }, 300)\n const handleFilterChange = useCallback(\n (e) => {\n const target = e.currentTarget\n if (target && target.value) {\n updateFilter(target.value)\n } else {\n updateFilter()\n }\n },\n [updateFilter],\n )\n const handleItemClick = useCallback(\n (e, item: Icon) => {\n setSelected(item.id)\n },\n [setSelected],\n )\n\n const renderItemContent = useCallback(\n function RenderItemContent(item: Icon, key: number) {\n const isSelected = Boolean(selected) && selected === item.id\n return (\n <Tooltip\n key={item.id ?? key}\n title={item.name}\n disableInteractive\n enterDelay={375}\n enterNextDelay={745}\n >\n <CardListItem\n item={item}\n selected={isSelected}\n // A real toggle, so the pick is announced and not only\n // painted (AGL-2486).\n role=\"button\"\n aria-pressed={isSelected}\n onClick={(e) => handleItemClick(e, item)}\n >\n <div>\n <MdiIcon fontSize=\"medium\" path={item.path} />\n </div>\n {isSelected ? (\n // Belt and braces on the fill: a tick reads as \"picked\"\n // even where the tint is subtle, and gives the state\n // something to assert against.\n <Box\n data-aglyn-icon-selected=\"\"\n sx={{\n position: 'absolute',\n top: 2,\n right: 2,\n lineHeight: 0,\n fontSize: 14,\n borderRadius: '50%',\n bgcolor: 'primary.contrastText',\n color: 'primary.main',\n }}\n >\n <MdiIcon fontSize=\"inherit\" path={mdiCheck.path} />\n </Box>\n ) : null}\n </CardListItem>\n </Tooltip>\n )\n },\n [selected, handleItemClick],\n )\n\n return (\n <Grid ref={ref} container>\n <Grid spacing={2} container sx={{\n alignItems: 'center'\n }}>\n <Grid>\n <ButtonBase\n onClick={handleButtonClick}\n disableRipple\n sx={(theme) => ({ fontSize: theme.typography.pxToRem(38) })}\n title={currentIcon.name || 'Choose icon'}\n >\n <MdiIcon fontSize=\"inherit\" path={currentIcon.path} />\n </ButtonBase>\n </Grid>\n <Grid\n size={{\n sm: \"grow\"\n }}>\n <Typography component=\"div\" variant=\"caption\">\n {label}\n </Typography>\n <MuiLink\n onClick={handleButtonClick}\n variant=\"button\"\n component=\"button\"\n color=\"primary\"\n sx={{\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'flex-start',\n }}\n >\n <span>\n <MdiIcon\n fontSize=\"inherit\"\n path={open ? mdiChevronUp.path : mdiChevronDown.path}\n />\n </span>\n <span>{currentIcon.name}</span>\n </MuiLink>\n </Grid>\n </Grid>\n <Grid size={12}>\n <StyledCollapse in={open}>\n <Grid spacing={2} container sx={{\n alignItems: \"center\"\n }}>\n <Grid size={12}>\n <MuiTextField\n onChange={handleFilterChange}\n placeholder=\"Search icons...\"\n size=\"small\"\n helperText={helperContent}\n fullWidth\n />\n </Grid>\n <Grid size=\"grow\">\n <Typography component=\"div\" variant=\"body2\">\n Selected icon: <b>{selectedIcon.name}</b>\n </Typography>\n <Typography\n component=\"div\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {hasPendingPick\n ? `Not applied yet — press Choose to replace ${currentIcon.name}.`\n : 'Pick an icon, then press Choose to apply it.'}\n </Typography>\n {currentIsUnknown ? (\n <Typography\n data-aglyn-icon-unknown=\"\"\n component=\"div\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {`This element carries ${currentValue}, which is not in the icon library — it renders on the site but has no preview here. Choosing another icon replaces it.`}\n </Typography>\n ) : null}\n </Grid>\n <Grid>\n <Button color=\"inherit\" onClick={handleCancelButtonClick}>\n Cancel\n </Button>\n </Grid>\n <Grid>\n <Button\n color=\"primary\"\n disabled={!hasPendingPick}\n onClick={handleChooseButtonClick}\n variant=\"contained\"\n >\n Choose\n </Button>\n </Grid>\n </Grid>\n\n <GridListWrapper>\n <GridList\n ref={gridRef as any}\n GridContainerProps={{ spacing: 1 }}\n GridItemProps={{ size: { xs: 2 } }}\n ListWrapperProps={{ className: classKeys.gridList }}\n items={visibleIcons}\n renderItemContent={renderItemContent}\n {...GridListProps}\n />\n {icons.length > visibleIcons.length ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', px: 1, pb: 1 }}\n >\n {`Showing ${visibleIcons.length} of ${icons.length} icons — search to narrow down.`}\n </Typography>\n ) : null}\n {allIcons.length === 0 ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', px: 1, pb: 1 }}\n >\n {'Loading icon catalog…'}\n </Typography>\n ) : null}\n </GridListWrapper>\n </StyledCollapse>\n </Grid>\n </Grid>\n )\n },\n)\nIconSelectControl.displayName = 'IconSelectControl'\n\n/**\n * The data-driven-forms field: everything form-shaped (validation message,\n * helper/description precedence, the final-form input) resolved here, and\n * the picking itself delegated to {@link IconSelectControl}.\n *\n * Sits in the same {@link FormFieldGrid} every other field does, so the corner\n * controls a schema asks for — the help tip, the opt-in clear, a caller's own\n * — are where they are on every other field rather than silently dropped.\n */\nconst IconSelectComponent = forwardRef<any, IconSelectProps>((props, ref) => {\n const {\n input,\n isReadOnly,\n isDisabled,\n placeholder,\n isRequired,\n label,\n helperText,\n description,\n validateOnMount,\n meta,\n inputProps,\n GridListProps,\n help,\n clearable,\n FormFieldGridProps = {},\n ...rest\n } = useFieldApi(props as UseFieldApiConfig)\n\n const invalidMessage = validationMessage(meta, validateOnMount)\n const helperContent = [\n invalidMessage,\n (meta.touched || validateOnMount) && meta.warning,\n helperText,\n description,\n ].find((i) => Boolean(i))\n\n // The picker can only ever choose an icon, so without this nothing takes\n // one back off again.\n const clear = buildFieldClear({\n clearable,\n label,\n hasValue: Boolean(input.value),\n locked: Boolean(isDisabled || isReadOnly),\n onClear: () => input.onChange(''),\n })\n\n return (\n <FormFieldGrid help={help} clear={clear} {...FormFieldGridProps}>\n <IconSelectControl\n ref={ref}\n value={input.value}\n onChange={input.onChange}\n label={label}\n helperText={helperContent}\n GridListProps={GridListProps}\n />\n </FormFieldGrid>\n )\n})\n\nIconSelectComponent.displayName = 'IconSelectComponent'\nIconSelectComponent.aglyn = true\n\nexport default IconSelectComponent\n"],"names":["DEFAULT_ICON","mdiCheck","mdiChevronDown","mdiChevronUp","CardListItem","MdiIcon","useMdiIconsFuzzy","GridList","generateComponentClassKeys","styled","useDebouncedCallback","Box","Button","ButtonBase","Collapse","Grid","Link","MuiLink","TextField","MuiTextField","Tooltip","Typography","forwardRef","useCallback","useEffect","useMemo","useRef","useState","FormFieldGrid","buildFieldClear","validationMessage","useFieldApi","iconUnset","id","name","iconUnknown","classKeys","StyledCollapse","theme","height","paddingTop","spacing","display","flexDirection","flexGrow","flexShrink","alignSelf","GridListWrapper","tv","vars","marginTop","border","palette","divider","borderRadius","shape","gridList","padding","IconSelectControl","props","ref","value","currentValue","onChange","label","helperText","helperContent","GridListProps","rest","open","setOpen","icons","allIcons","applyFilter","clearFilter","selected","setSelected","MAX_RENDERED_ICONS","visibleIcons","length","slice","gridRef","current","scrollToIndex","index","currentIcon","selectedIcon","resolve","find","icon","currentIsUnknown","Boolean","some","handleButtonClick","prev","hasPendingPick","handleCancelButtonClick","handleChooseButtonClick","updateFilter","handleFilterChange","e","target","currentTarget","handleItemClick","item","renderItemContent","RenderItemContent","key","isSelected","title","disableInteractive","enterDelay","enterNextDelay","role","aria-pressed","onClick","div","fontSize","path","data-aglyn-icon-selected","sx","position","top","right","lineHeight","bgcolor","color","container","alignItems","disableRipple","typography","pxToRem","size","sm","component","variant","justifyContent","span","in","placeholder","fullWidth","b","data-aglyn-icon-unknown","disabled","GridContainerProps","GridItemProps","xs","ListWrapperProps","className","items","px","pb","displayName","IconSelectComponent","input","isReadOnly","isDisabled","isRequired","description","validateOnMount","meta","inputProps","help","clearable","FormFieldGridProps","invalidMessage","touched","warning","i","clear","hasValue","locked","onClear","aglyn"],"mappings":"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"}
|