strapi-plugin-icons-field
Version:
A customizable plugin for Strapi to integrate an icon field into your content types.
1 lines • 19.4 kB
Source Map (JSON)
{"version":3,"file":"IconSelect-d7-HnBJE.mjs","sources":["../../admin/src/components/Icon.tsx","../../admin/src/components/IconSelect.tsx"],"sourcesContent":["import React, { HTMLAttributes } from 'react';\nimport parse from 'html-react-parser';\n\ninterface RenderSvgProps extends HTMLAttributes<SVGElement> {\n icon?: string;\n}\n\nexport default function Icon({ icon, ...props }: RenderSvgProps) {\n const parsedElement = icon && parse(icon.trim());\n if (parsedElement && React.isValidElement(parsedElement)) {\n return React.cloneElement(parsedElement, props);\n }\n return null;\n}\n","import React, { useEffect, useMemo, useState } from 'react';\nimport { Box, Typography, Button, Modal, Searchbar } from '@strapi/design-system';\nimport { useFetchClient } from '@strapi/strapi/admin';\nimport { PLUGIN_ID } from '../pluginId';\nimport Icon from './Icon';\nimport { useTheme } from 'styled-components';\nimport styled from 'styled-components';\nimport { Cross, CaretDown } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { Field } from '@strapi/design-system';\n\nconst ThemeableIcon = styled.span<{ $color: string }>`\n display: contents;\n\n & > svg {\n color: ${(p) => p.$color};\n }\n\n & > svg[stroke]:not([stroke='none']) {\n stroke: ${(p) => p.$color};\n }\n\n & > svg:not([stroke]) {\n fill: ${(p) => p.$color};\n }\n\n & > svg *[fill]:not([fill='none']) {\n fill: ${(p) => p.$color};\n }\n\n & > svg *[stroke]:not([stroke='none']) {\n stroke: ${(p) => p.$color};\n fill: none;\n }\n`;\n\nconst hasOwnColors = (svg: string): boolean =>\n /(?:fill|stroke)\\s*=\\s*[\"'](?:#|rgb|hsl|url\\(#)/i.test(svg) ||\n /(?:fill|stroke)\\s*:\\s*(?:#|rgb|hsl)/i.test(svg);\n\nconst IconPreview = ({\n svg,\n color,\n style,\n}: {\n svg: string;\n color: string;\n style?: React.CSSProperties;\n}) => {\n const icon = <Icon icon={svg} style={style} />;\n if (hasOwnColors(svg)) return icon;\n return <ThemeableIcon $color={color}>{icon}</ThemeableIcon>;\n};\n\ntype IconSelectProps = {\n label: string;\n hint: string;\n attribute: any;\n description: any;\n disabled: boolean;\n error: any;\n intlLabel: any;\n name: string;\n onChange: (event: { target: { name: string; value: string; type: string } }) => void;\n placeholder: any;\n required: boolean;\n value: string;\n};\n\nconst IconSelect = (props: IconSelectProps) => {\n const theme = useTheme() as any;\n const intl = useIntl();\n\n const { label, hint, attribute, disabled, error, name, onChange, placeholder, required, value } =\n props;\n\n const [icons, setIcons] = useState<\n Array<{ folder: string; icons: Array<{ name: string; svg: string }> }>\n >([]);\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n\n const { get } = useFetchClient();\n\n const toggleModal = () => setIsModalOpen((prev) => !prev);\n\n useEffect(() => {\n const fetchIcons = async () => {\n try {\n let url = `/${PLUGIN_ID}/icons`;\n if (attribute?.options?.selection) {\n if (Array.isArray(attribute.options.selection)) {\n url += `?selection=${encodeURIComponent(attribute.options.selection.join(','))}`;\n } else {\n url += `?selection=${encodeURIComponent(attribute.options.selection)}`;\n }\n }\n const { data } = await get(url);\n setIcons(data || []);\n } catch (error) {\n console.error('Error fetching icons:', error);\n }\n };\n fetchIcons();\n }, [attribute?.options?.selection]);\n\n const outputFormat: 'svg' | 'name' = attribute?.options?.outputFormat || 'svg';\n\n const handleChange = (icon: { name: string; svg: string }) => {\n const storedValue = outputFormat === 'name' ? icon.name : icon.svg;\n onChange({ target: { name, value: storedValue, type: attribute.type } });\n };\n\n const allIcons = useMemo(() => icons.flatMap((group) => group.icons), [icons]);\n\n const selectedIconData = useMemo(() => {\n if (!value) return undefined;\n if (outputFormat === 'name') {\n return allIcons.find((icon) => icon.name === value);\n }\n return allIcons.find((icon) => icon.svg === value);\n }, [value, allIcons, outputFormat]);\n\n const filteredIcons = useMemo(() => {\n if (!searchQuery.trim()) return icons;\n const query = searchQuery.toLowerCase();\n return icons\n .map((group) => ({\n ...group,\n icons: group.icons.filter((icon) => icon.name.toLowerCase().includes(query)),\n }))\n .filter((group) => group.icons.length > 0);\n }, [icons, searchQuery]);\n\n const handleUnselectedIcon = (e: React.MouseEvent<HTMLButtonElement>) => {\n e.stopPropagation();\n onChange({ target: { name, value: '', type: attribute.type } });\n };\n\n return (\n <Field.Root error={error} hint={hint} required={required}>\n <Field.Label>{label}</Field.Label>\n <Modal.Root\n labelledBy=\"icon-select-modal-title\"\n open={isModalOpen}\n onOpenChange={setIsModalOpen}\n >\n <Modal.Trigger>\n <Box\n display=\"flex\"\n style={{ alignItems: 'center', gap: '12px', position: 'relative', width: '100%' }}\n >\n <div style={{ position: 'relative', flex: 1 }}>\n <Button\n variant=\"tertiary\"\n disabled={disabled}\n style={{\n gap: '8px',\n height: '42px',\n width: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n }}\n >\n {selectedIconData ? (\n <>\n <IconPreview\n svg={selectedIconData.svg}\n color={theme.colors?.neutral800 || 'currentColor'}\n style={{ height: '24px', display: 'block' }}\n />\n <Typography variant=\"pi\">{selectedIconData.name}</Typography>\n </>\n ) : (\n <Typography style={{ fontWeight: '400' }}>\n {placeholder ??\n intl.formatMessage({\n id: 'icons-field.select',\n defaultMessage: 'Select an icon',\n })}\n </Typography>\n )}\n <CaretDown\n color={theme.colors?.neutral500}\n style={{\n position: 'absolute',\n right: '16px',\n height: '16px',\n width: '16px',\n }}\n />\n </div>\n </Button>\n </div>\n {selectedIconData && (\n <button\n type=\"button\"\n onClick={handleUnselectedIcon}\n style={{\n position: 'absolute',\n right: '44px',\n height: '16px',\n width: '16px',\n cursor: 'pointer',\n zIndex: 3,\n }}\n >\n <Cross color={theme.colors?.neutral500} style={{ height: '100%', width: '100%' }} />\n </button>\n )}\n </Box>\n </Modal.Trigger>\n <Modal.Content>\n <Modal.Header>\n <Typography fontWeight=\"bold\" textColor=\"neutral800\" as=\"h2\" id=\"title\">\n {intl.formatMessage({\n id: 'icons-field.modal.title',\n defaultMessage: 'Select an icon',\n })}\n </Typography>\n </Modal.Header>\n <Modal.Body>\n <Box marginBottom={4}>\n <Searchbar\n name=\"icon-search\"\n value={searchQuery}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setSearchQuery(e.target.value)\n }\n onClear={() => setSearchQuery('')}\n clearLabel={intl.formatMessage({\n id: 'icons-field.modal.search.clear',\n defaultMessage: 'Clear search',\n })}\n placeholder={intl.formatMessage({\n id: 'icons-field.modal.search.placeholder',\n defaultMessage: 'Search icons...',\n })}\n >\n {intl.formatMessage({\n id: 'icons-field.modal.search.placeholder',\n defaultMessage: 'Search icons...',\n })}\n </Searchbar>\n </Box>\n {filteredIcons.length === 0 && (\n <Typography textColor=\"neutral500\">\n {intl.formatMessage({\n id: 'icons-field.modal.search.empty',\n defaultMessage: 'No icons found.',\n })}\n </Typography>\n )}\n {filteredIcons.map((group) => (\n <Box key={group.folder} marginBottom={6}>\n <Typography\n fontWeight=\"bold\"\n textColor=\"neutral800\"\n as=\"h3\"\n id=\"title\"\n marginBottom={2}\n >\n {group.folder}\n </Typography>\n <Box display=\"flex\" marginTop={2} style={{ flexWrap: 'wrap', gap: 6 }}>\n {group.icons.map((icon) => {\n const isSelected =\n outputFormat === 'name' ? value === icon.name : value === icon.svg;\n return (\n <Box\n key={icon.name}\n as=\"button\"\n type=\"button\"\n aria-label={icon.name}\n onClick={() => {\n handleChange(icon);\n toggleModal();\n }}\n style={{\n alignItems: 'center',\n aspectRatio: '1/1',\n background: isSelected\n ? theme.colors?.primary100\n : theme.colors?.neutral100,\n border: isSelected\n ? `2px solid ${theme.colors?.primary600}`\n : `1px solid ${theme.colors?.neutral200}`,\n borderRadius: '4px',\n cursor: 'pointer',\n display: 'flex',\n flexDirection: 'column',\n gap: '2px',\n height: '100%',\n justifyContent: 'center',\n padding: '12px',\n width: '85px',\n }}\n >\n <IconPreview\n svg={icon.svg}\n color={theme.colors?.neutral800 || 'currentColor'}\n style={{\n aspectRatio: '1/1',\n height: '100%',\n maxWidth: '25px',\n width: '100%',\n }}\n />\n {attribute?.options?.showIconLabel && (\n <Typography\n variant=\"pi\"\n fontWeight=\"bold\"\n textColor=\"neutral800\"\n style={{ marginTop: 8 }}\n >\n {icon.name}\n </Typography>\n )}\n </Box>\n );\n })}\n </Box>\n </Box>\n ))}\n </Modal.Body>\n <Modal.Footer>\n <Modal.Close>\n <Button variant=\"tertiary\">\n {intl.formatMessage({\n id: 'icons-field.modal.close',\n defaultMessage: 'Close window',\n })}\n </Button>\n </Modal.Close>\n </Modal.Footer>\n </Modal.Content>\n </Modal.Root>\n <Field.Hint />\n </Field.Root>\n );\n};\n\nexport default IconSelect;\n"],"names":["error"],"mappings":"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"}