@starefossen/sanity-plugin-inline-svg-input
Version:
Sanity plugin to upload and preview inline SVGs
1 lines • 10.9 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/InlineSvgInput.tsx","../src/inlineSvgType.ts","../src/InlineSvgPreviewComponent.tsx","../src/InlineSvgPreviewItem.tsx","../src/index.ts"],"sourcesContent":["import { ChangeEvent, useEffect, useRef } from 'react'\nimport { set, StringInputProps, unset } from 'sanity'\nimport styled, { css } from 'styled-components'\nimport DOMPurify from 'isomorphic-dompurify'\nimport { ThemeProvider, usePrefersDark, useTheme } from '@sanity/ui'\n\nconst Container = styled.div`\n --svg-bg-color: rgba(23, 23, 23, 0.05);\n\n background-image: linear-gradient(45deg, var(--svg-bg-color) 25%, transparent 25%),\n linear-gradient(-45deg, var(--svg-bg-color) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--svg-bg-color) 75%),\n linear-gradient(-45deg, transparent 75%, var(--svg-bg-color) 75%);\n background-size: 20px 20px;\n background-position:\n 0 0,\n 0 10px,\n 10px -10px,\n -10px 0;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 250px;\n border: 1px solid var(--card-border-color);\n border-radius: 3px;\n position: relative;\n\n &:focus-within {\n border-color: var(--card-focus-ring-color);\n }\n\n &.dark {\n --svg-bg-color: rgb(255, 255, 255, 0.1);\n }\n\n input[type='file'] {\n opacity: 0;\n z-index: -1;\n position: absolute;\n }\n\n input[type='file']:focus + label {\n outline: 2px solid;\n }\n\n * {\n box-sizing: border-box;\n }\n`\n\nconst SvgWrapper = styled.div`\n display: flex;\n max-height: 320px;\n\n > div {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n svg {\n max-height: 80%;\n max-width: 80%;\n display: flex;\n margin: auto;\n width: 100%;\n height: 100%;\n }\n`\n\nconst ButtonStyle = css`\n border-radius: 0.1875rem;\n font: inherit;\n outline: none;\n border: 0;\n padding: 0.85rem 1.75rem;\n margin: 0;\n color: #fff;\n cursor: pointer;\n font-size: 1rem;\n font-weight: 500;\n`\n\nconst AddButton = styled.label`\n ${ButtonStyle};\n color: #fff;\n background-color: #4285f4;\n\n &:hover {\n background-color: #3a6fc8;\n }\n`\n\nconst RemoveButton = styled.button`\n ${ButtonStyle};\n position: absolute;\n top: 10px;\n right: 10px;\n color: #fff;\n background-color: #db4437;\n\n &:hover {\n background-color: #b43b31;\n }\n`\n\nexport const InlineSvgInput = ({ id, value, schemaType, onChange, focused }: StringInputProps) => {\n const inputRef = useRef<HTMLInputElement>(null)\n const prefersDark = usePrefersDark()\n const scheme = prefersDark ? 'dark' : 'light'\n const theme = useTheme()\n const isDarkMode = theme.sanity.color.dark\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target?.files?.[0]\n if (!file) return\n const reader = new FileReader()\n reader.onload = (readerEvent) => {\n if (!readerEvent.target) return\n onChange(set(readerEvent.target.result))\n }\n reader.readAsText(file)\n }\n\n const focus = () => {\n if (!inputRef.current) return\n inputRef.current.focus()\n }\n\n useEffect(() => {\n if (focused) focus()\n }, [focused])\n\n const clickedRemoveSvg = () => {\n \n if (confirm('Are you sure you want to remove the SVG?')) {\n onChange(unset())\n if (inputRef.current) inputRef.current.value = ''\n }\n return false\n }\n\n return (\n <Container className={isDarkMode ? 'dark' : 'light'}>\n <ThemeProvider scheme={scheme}>\n <input\n accept=\".svg\"\n id={id}\n ref={inputRef}\n type=\"file\"\n placeholder={schemaType.placeholder}\n onChange={handleChange}\n name={'inline-svg'}\n />\n\n {!value && <AddButton htmlFor={id}>Upload SVG</AddButton>}\n\n {value && (\n <SvgWrapper>\n <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(value) }} />\n\n <RemoveButton onClick={clickedRemoveSvg}>Remove SVG</RemoveButton>\n </SvgWrapper>\n )}\n </ThemeProvider>\n </Container>\n )\n}\n","import { defineType } from 'sanity'\nimport { InlineSvgInput } from './InlineSvgInput'\n\nexport const inlineSvgType = defineType({\n name: 'inlineSvg',\n title: 'Inline SVG',\n type: 'string',\n components: {\n input: InlineSvgInput,\n },\n})\n","import DOMPurify from 'isomorphic-dompurify'\nimport styled from 'styled-components'\nimport { CSSProperties } from 'react'\n\nconst InlineSvgPreview = styled.div`\n svg {\n width: 100%;\n height: 100%;\n }\n`\n\nexport const InlineSvgPreviewComponent = ({\n value,\n className,\n style,\n}: {\n value?: string\n className?: string\n style?: CSSProperties\n}) => {\n if (!value) {\n return null\n }\n\n return (\n <InlineSvgPreview\n dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(value) }}\n className={className}\n style={style}\n />\n )\n}\n","import { InlineSvgPreviewComponent } from './InlineSvgPreviewComponent'\nimport styled, { css } from 'styled-components'\nimport { PreviewLayoutKey, PreviewProps } from 'sanity'\n\nconst Container = styled.div`\n display: flex;\n align-items: center;\n box-sizing: border-box;\n\n * {\n box-sizing: border-box;\n }\n`\n\nconst IconStyle = css`\n width: 35px;\n height: 35px;\n margin-right: 8px;\n flex-shrink: 0;\n`\n\nconst Icon = styled(InlineSvgPreviewComponent)`\n ${IconStyle}\n`\n\nconst IconStub = styled.div`\n ${IconStyle}\n`\n\nconst Title = styled.span<{ empty?: boolean }>`\n display: block;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n overflow: clip;\n font-size: 1rem;\n line-height: calc(21 / 16);\n color: ${({ empty }) => (empty ? '#6e7683' : 'inherit')}};\n`\n\nconst Subtitle = styled.span`\n display: block;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n overflow: clip;\n font-size: 0.8125rem;\n line-height: calc(17 / 13);\n color: #6e7683;\n`\n\nconst TextContainer = styled.div`\n overflow: hidden;\n`\n\nexport const InlineSvgPreviewItem = ({\n icon,\n title,\n subtitle,\n}: {\n icon?: string | null\n} & Pick<PreviewProps<PreviewLayoutKey>, 'title' | 'subtitle'>) => {\n if ((title && typeof title !== 'string') || (subtitle && typeof subtitle !== 'string')) {\n return (\n <Container>\n `InlineSvgPreviewItem` supports only string values for `title` and `subtitle` props.\n </Container>\n )\n }\n\n return (\n <Container>\n {icon ? <Icon value={icon} /> : <IconStub />}\n\n <TextContainer>\n {title ? <Title>{title}</Title> : <Title empty>Untitled</Title>}\n {subtitle && <Subtitle>{subtitle}</Subtitle>}\n </TextContainer>\n </Container>\n )\n}\n","import { definePlugin } from 'sanity'\nimport { inlineSvgType } from './inlineSvgType'\nimport { InlineSvgPreviewComponent } from './InlineSvgPreviewComponent'\nimport { InlineSvgPreviewItem } from './InlineSvgPreviewItem'\n\nexport interface InlineSvgInputConfig {}\n\n/**\n * Usage in `sanity.config.ts` (or .js)\n *\n * ```ts\n * import {defineConfig} from 'sanity'\n * import {inlineSvgInput} from '@focus-reactive/sanity-plugin-inline-svg-input'\n *\n * export default defineConfig({\n * // ...\n * plugins: [inlineSvgInput()],\n * })\n * ```\n */\nexport const inlineSvgInput = definePlugin<InlineSvgInputConfig | void>((config = {}) => {\n return {\n name: 'sanity-plugin-inline-svg-input',\n schema: {\n types: [inlineSvgType],\n },\n }\n})\n\nexport { InlineSvgPreviewComponent, InlineSvgPreviewItem }\n"],"names":["Container","styled","css","useRef","usePrefersDark","useTheme","set","useEffect","unset","jsx","jsxs","ThemeProvider","DOMPurify","defineType","definePlugin"],"mappings":"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"}