ui: Constants refactor (#26908)
* refactor: Constants * refactor: Constants/Enums cleanup * refactor: Constant objects instead of multiple single value constants * refactor: Cleanup constants
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import ChevronDown from '@lucide/svelte/icons/chevron-down';
|
||||
import * as Collapsible from '$lib/components/ui/collapsible/index.js';
|
||||
import { cn } from '$lib/components/ui/utils';
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { Component } from 'svelte';
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
} from '$lib/components/app';
|
||||
import {
|
||||
BOOL_TRUE_STRING,
|
||||
CODE_BLOCK_HEADER_CLASS,
|
||||
CODE_BLOCK_CLASS,
|
||||
DATA_ERROR_BOUND_ATTR,
|
||||
DATA_ERROR_HANDLED_ATTR,
|
||||
DIAGRAM_VIEW_MODE_ATTR,
|
||||
@@ -39,15 +39,8 @@
|
||||
MERMAID_SYNTAX_ATTR,
|
||||
MERMAID_WRAPPER_CLASS,
|
||||
SETTINGS_KEYS,
|
||||
SVG_BLOCK_CLASS,
|
||||
SVG_INLINE_SHADOW_STYLE,
|
||||
SVG_LANGUAGE,
|
||||
SVG_RENDERED_ATTR,
|
||||
SVG_SOURCE_ATTR,
|
||||
SVG_TAG_PREFIX,
|
||||
SVG_WRAPPER_CLASS,
|
||||
TOGGLE_SOURCE_BTN_CLASS,
|
||||
XML_LANGUAGE
|
||||
SVG,
|
||||
TOGGLE_SOURCE_BTN_CLASS
|
||||
} from '$lib/constants';
|
||||
import { ColorMode, UrlProtocol } from '$lib/enums';
|
||||
import { FileTypeText } from '$lib/enums/files.enums';
|
||||
@@ -105,9 +98,9 @@
|
||||
|
||||
if (!block) return null;
|
||||
|
||||
if (block.language === SVG_LANGUAGE) return block.code;
|
||||
if (block.language === SVG.LANGUAGE) return block.code;
|
||||
|
||||
if (block.language === XML_LANGUAGE && block.code.trimStart().startsWith(SVG_TAG_PREFIX))
|
||||
if (block.language === SVG.XML_LANGUAGE && block.code.trimStart().startsWith(SVG.TAG_PREFIX))
|
||||
return block.code;
|
||||
|
||||
return null;
|
||||
@@ -137,7 +130,7 @@
|
||||
|
||||
// Mount the streaming svg into its shadow host on every chunk so it renders live
|
||||
$effect(() => {
|
||||
if (streamingSvgHost) mountSvgShadow(streamingSvgHost, liveSvgHtml, SVG_INLINE_SHADOW_STYLE);
|
||||
if (streamingSvgHost) mountSvgShadow(streamingSvgHost, liveSvgHtml, SVG.INLINE_SHADOW_STYLE);
|
||||
});
|
||||
|
||||
let streamingCodeScrollContainer = $state<HTMLDivElement>();
|
||||
@@ -535,7 +528,7 @@
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const wrapper = toggleBtn.closest(`.${MERMAID_WRAPPER_CLASS}, .${SVG_WRAPPER_CLASS}`);
|
||||
const wrapper = toggleBtn.closest(`.${MERMAID_WRAPPER_CLASS}, .${SVG.WRAPPER_CLASS}`);
|
||||
|
||||
if (!wrapper) return;
|
||||
|
||||
@@ -593,16 +586,16 @@
|
||||
}
|
||||
|
||||
// Check if clicking on copy or preview button in svg block
|
||||
const svgCopyBtn = target.closest(`.${SVG_WRAPPER_CLASS} .copy-code-btn`);
|
||||
const svgPreviewBtn = target.closest(`.${SVG_WRAPPER_CLASS} .preview-code-btn`);
|
||||
const svgCopyBtn = target.closest(`.${SVG.WRAPPER_CLASS} .copy-code-btn`);
|
||||
const svgPreviewBtn = target.closest(`.${SVG.WRAPPER_CLASS} .preview-code-btn`);
|
||||
|
||||
if (svgCopyBtn || svgPreviewBtn) {
|
||||
const wrapper = target.closest(`.${SVG_WRAPPER_CLASS}`);
|
||||
const wrapper = target.closest(`.${SVG.WRAPPER_CLASS}`);
|
||||
|
||||
if (!wrapper) return;
|
||||
|
||||
const preElement = wrapper.querySelector<HTMLElement>(
|
||||
`pre.${SVG_BLOCK_CLASS}[${SVG_SOURCE_ATTR}]`
|
||||
`pre.${SVG.BLOCK_CLASS}[${SVG.SOURCE_ATTR}]`
|
||||
);
|
||||
|
||||
if (!preElement) return;
|
||||
@@ -611,7 +604,7 @@
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
try {
|
||||
await copyToClipboard(preElement.getAttribute(SVG_SOURCE_ATTR) ?? '');
|
||||
await copyToClipboard(preElement.getAttribute(SVG.SOURCE_ATTR) ?? '');
|
||||
} catch (error) {
|
||||
console.error('Failed to copy svg source:', error);
|
||||
}
|
||||
@@ -622,7 +615,7 @@
|
||||
if (svgPreviewBtn) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
mermaidPreviewSvgHtml = sanitizeSvg(preElement.getAttribute(SVG_SOURCE_ATTR) ?? '');
|
||||
mermaidPreviewSvgHtml = sanitizeSvg(preElement.getAttribute(SVG.SOURCE_ATTR) ?? '');
|
||||
svgPreviewLive = false;
|
||||
mermaidPreviewOpen = true;
|
||||
|
||||
@@ -633,14 +626,14 @@
|
||||
// A click on the header chrome targets the action buttons, never the
|
||||
// diagram. Guard so a header click can not fall through to the click to
|
||||
// zoom branches below, whatever the scroll position or stacking.
|
||||
if (target.closest(`.${CODE_BLOCK_HEADER_CLASS}`)) return;
|
||||
if (target.closest(`.${CODE_BLOCK_CLASS.HEADER}`)) return;
|
||||
|
||||
// Open preview when clicking the svg block itself. A final block carries its
|
||||
// source, a streaming block does not and is mirrored live into the dialog.
|
||||
const svgEl = target.closest(`.${SVG_BLOCK_CLASS}`);
|
||||
const svgEl = target.closest(`.${SVG.BLOCK_CLASS}`);
|
||||
|
||||
if (svgEl) {
|
||||
const source = svgEl.getAttribute(SVG_SOURCE_ATTR);
|
||||
const source = svgEl.getAttribute(SVG.SOURCE_ATTR);
|
||||
|
||||
if (source !== null) {
|
||||
mermaidPreviewSvgHtml = sanitizeSvg(source);
|
||||
@@ -739,15 +732,15 @@
|
||||
if (!containerRef) return;
|
||||
|
||||
const nodes = containerRef.querySelectorAll<HTMLElement>(
|
||||
`pre.${SVG_BLOCK_CLASS}:not([${SVG_RENDERED_ATTR}])`
|
||||
`pre.${SVG.BLOCK_CLASS}:not([${SVG.RENDERED_ATTR}])`
|
||||
);
|
||||
|
||||
if (nodes.length === 0) return;
|
||||
|
||||
nodes.forEach((node) => {
|
||||
node.setAttribute(SVG_RENDERED_ATTR, 'true');
|
||||
node.setAttribute(SVG.RENDERED_ATTR, 'true');
|
||||
|
||||
const source = node.getAttribute(SVG_SOURCE_ATTR) ?? node.textContent ?? '';
|
||||
const source = node.getAttribute(SVG.SOURCE_ATTR) ?? node.textContent ?? '';
|
||||
const clean = sanitizeSvg(source);
|
||||
|
||||
if (clean) {
|
||||
@@ -755,7 +748,7 @@
|
||||
const host = document.createElement('div');
|
||||
|
||||
node.appendChild(host);
|
||||
mountSvgShadow(host, clean, SVG_INLINE_SHADOW_STYLE);
|
||||
mountSvgShadow(host, clean, SVG.INLINE_SHADOW_STYLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -919,7 +912,7 @@
|
||||
</div>
|
||||
{#if liveSvgHtml}
|
||||
<div class="svg-scroll-container">
|
||||
<div class={SVG_BLOCK_CLASS}>
|
||||
<div class={SVG.BLOCK_CLASS}>
|
||||
<div bind:this={streamingSvgHost}></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+8
-14
@@ -4,17 +4,11 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
CODE_BLOCK_ACTIONS_CLASS,
|
||||
CODE_BLOCK_HEADER_CLASS,
|
||||
CODE_BLOCK_SCROLL_CONTAINER_CLASS,
|
||||
CODE_BLOCK_CLASS,
|
||||
CODE_ICON_SVG,
|
||||
CODE_LANGUAGE_CLASS,
|
||||
COPY_CODE_BTN_CLASS,
|
||||
COPY_ICON_SVG,
|
||||
DIAGRAM_SOURCE_CLASS,
|
||||
PREVIEW_CODE_BTN_CLASS,
|
||||
PREVIEW_ICON_SVG,
|
||||
RELATIVE_CLASS,
|
||||
TOGGLE_SOURCE_BTN_CLASS
|
||||
} from '$lib/constants';
|
||||
import type { Element, ElementContent } from 'hast';
|
||||
@@ -65,7 +59,7 @@ export function createButton(
|
||||
* Creates a copy button element.
|
||||
*/
|
||||
export function createCopyButton(id: string, idAttribute: string, title: string = 'Copy'): Element {
|
||||
return createButton(COPY_CODE_BTN_CLASS, title, COPY_ICON_SVG, id, idAttribute);
|
||||
return createButton(CODE_BLOCK_CLASS.COPY_BTN, title, COPY_ICON_SVG, id, idAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +70,7 @@ export function createPreviewButton(
|
||||
idAttribute: string,
|
||||
title: string = 'Preview'
|
||||
): Element {
|
||||
return createButton(PREVIEW_CODE_BTN_CLASS, title, PREVIEW_ICON_SVG, id, idAttribute);
|
||||
return createButton(CODE_BLOCK_CLASS.PREVIEW_BTN, title, PREVIEW_ICON_SVG, id, idAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +114,7 @@ export function createSourceView(
|
||||
type: 'element'
|
||||
}
|
||||
],
|
||||
properties: { className: [DIAGRAM_SOURCE_CLASS, CODE_BLOCK_SCROLL_CONTAINER_CLASS] },
|
||||
properties: { className: [DIAGRAM_SOURCE_CLASS, CODE_BLOCK_CLASS.SCROLL_CONTAINER] },
|
||||
tagName: 'div',
|
||||
type: 'element'
|
||||
};
|
||||
@@ -134,7 +128,7 @@ export function createBlockHeader(
|
||||
id: string,
|
||||
idAttribute: string,
|
||||
actions: Element[],
|
||||
languageClassName: string = CODE_LANGUAGE_CLASS
|
||||
languageClassName: string = CODE_BLOCK_CLASS.LANGUAGE
|
||||
): Element {
|
||||
return {
|
||||
children: [
|
||||
@@ -146,12 +140,12 @@ export function createBlockHeader(
|
||||
},
|
||||
{
|
||||
children: actions,
|
||||
properties: { className: [CODE_BLOCK_ACTIONS_CLASS] },
|
||||
properties: { className: [CODE_BLOCK_CLASS.ACTIONS] },
|
||||
tagName: 'div',
|
||||
type: 'element'
|
||||
}
|
||||
],
|
||||
properties: { className: [CODE_BLOCK_HEADER_CLASS] },
|
||||
properties: { className: [CODE_BLOCK_CLASS.HEADER] },
|
||||
tagName: 'div',
|
||||
type: 'element'
|
||||
};
|
||||
@@ -185,7 +179,7 @@ export function createWrapper(
|
||||
return {
|
||||
children: [header, createScrollContainer(preElement, scrollContainerClass), ...extraChildren],
|
||||
properties: {
|
||||
className: [wrapperClass, RELATIVE_CLASS],
|
||||
className: [wrapperClass, CODE_BLOCK_CLASS.RELATIVE],
|
||||
...additionalAttributes
|
||||
} as Element['properties'],
|
||||
tagName: 'div',
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@ import {
|
||||
createWrapper,
|
||||
generateBlockId
|
||||
} from './code-block-utils';
|
||||
import { CODE_BLOCK_SCROLL_CONTAINER_CLASS, CODE_BLOCK_WRAPPER_CLASS } from '$lib/constants';
|
||||
import { CODE_BLOCK_CLASS } from '$lib/constants';
|
||||
import type { Element, ElementContent, Root } from 'hast';
|
||||
import type { Plugin } from 'unified';
|
||||
import { visit } from 'unist-util-visit';
|
||||
@@ -78,8 +78,8 @@ export const rehypeEnhanceCodeBlocks: Plugin<[], Root> = () => {
|
||||
const wrapper = createWrapper(
|
||||
header,
|
||||
node,
|
||||
CODE_BLOCK_WRAPPER_CLASS,
|
||||
CODE_BLOCK_SCROLL_CONTAINER_CLASS
|
||||
CODE_BLOCK_CLASS.WRAPPER,
|
||||
CODE_BLOCK_CLASS.SCROLL_CONTAINER
|
||||
);
|
||||
|
||||
// Replace pre with wrapper in parent
|
||||
|
||||
+13
-22
@@ -19,16 +19,7 @@ import {
|
||||
generateBlockId
|
||||
} from './code-block-utils';
|
||||
import type { DiagramPreData } from './pre-transform';
|
||||
import {
|
||||
DIAGRAM_VIEW_MODE_ATTR,
|
||||
DIAGRAM_VIEW_RENDERED,
|
||||
SVG_BLOCK_CLASS,
|
||||
SVG_ID_ATTR,
|
||||
SVG_LANGUAGE,
|
||||
SVG_SCROLL_CONTAINER_CLASS,
|
||||
SVG_SOURCE_ATTR,
|
||||
SVG_WRAPPER_CLASS
|
||||
} from '$lib/constants';
|
||||
import { DIAGRAM_VIEW_MODE_ATTR, DIAGRAM_VIEW_RENDERED, SVG } from '$lib/constants';
|
||||
import type { Element, ElementContent, Root } from 'hast';
|
||||
import type { Plugin } from 'unified';
|
||||
import { visit } from 'unist-util-visit';
|
||||
@@ -48,11 +39,11 @@ export const rehypeEnhanceSvgBlocks: Plugin<[], Root> = () => {
|
||||
|
||||
if (!Array.isArray(className)) return;
|
||||
|
||||
const isSvg = className.some((cls) => typeof cls === 'string' && cls === SVG_BLOCK_CLASS);
|
||||
const isSvg = className.some((cls) => typeof cls === 'string' && cls === SVG.BLOCK_CLASS);
|
||||
|
||||
if (!isSvg) return;
|
||||
|
||||
const svgId = generateBlockId(SVG_LANGUAGE, 'idxSvgBlock');
|
||||
const svgId = generateBlockId(SVG.LANGUAGE, 'idxSvgBlock');
|
||||
// Extract the svg source (text content of the pre element)
|
||||
const svgSource = node.children
|
||||
.map((child) => {
|
||||
@@ -65,26 +56,26 @@ export const rehypeEnhanceSvgBlocks: Plugin<[], Root> = () => {
|
||||
// Store the svg source in data attribute for copy and render
|
||||
node.properties = {
|
||||
...node.properties,
|
||||
[SVG_ID_ATTR]: svgId,
|
||||
[SVG_SOURCE_ATTR]: svgSource
|
||||
[SVG.ID_ATTR]: svgId,
|
||||
[SVG.SOURCE_ATTR]: svgSource
|
||||
};
|
||||
|
||||
const actions = [
|
||||
createCopyButton(svgId, SVG_ID_ATTR, 'Copy svg source'),
|
||||
createToggleSourceButton(svgId, SVG_ID_ATTR, 'Toggle svg source'),
|
||||
createPreviewButton(svgId, SVG_ID_ATTR, 'Preview svg')
|
||||
createCopyButton(svgId, SVG.ID_ATTR, 'Copy svg source'),
|
||||
createToggleSourceButton(svgId, SVG.ID_ATTR, 'Toggle svg source'),
|
||||
createPreviewButton(svgId, SVG.ID_ATTR, 'Preview svg')
|
||||
];
|
||||
const header = createBlockHeader(SVG_LANGUAGE, svgId, SVG_ID_ATTR, actions);
|
||||
const header = createBlockHeader(SVG.LANGUAGE, svgId, SVG.ID_ATTR, actions);
|
||||
const preservedCode = (node.data as DiagramPreData | undefined)?.sourceCode;
|
||||
const sourceView = createSourceView(preservedCode, svgSource, SVG_LANGUAGE);
|
||||
const sourceView = createSourceView(preservedCode, svgSource, SVG.LANGUAGE);
|
||||
const wrapper = createWrapper(
|
||||
header,
|
||||
node,
|
||||
SVG_WRAPPER_CLASS,
|
||||
SVG_SCROLL_CONTAINER_CLASS,
|
||||
SVG.WRAPPER_CLASS,
|
||||
SVG.SCROLL_CONTAINER_CLASS,
|
||||
{
|
||||
[DIAGRAM_VIEW_MODE_ATTR]: DIAGRAM_VIEW_RENDERED,
|
||||
[SVG_ID_ATTR]: svgId
|
||||
[SVG.ID_ATTR]: svgId
|
||||
},
|
||||
[sourceView]
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Rehype plugin that rewrites `file://` markdown anchors into the inline
|
||||
* mention chip, sharing the class string with the contenteditable
|
||||
* tokenizer via `$lib/constants/mention-badge`.
|
||||
* tokenizer via `$lib/constants`.
|
||||
*
|
||||
* The chip is presentational: `file://` navigation is blocked from
|
||||
* http(s) pages, so the anchor becomes a plain `<span>` (no link role,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createPreTransform } from './pre-transform';
|
||||
import { SVG_BLOCK_CLASS, SVG_LANGUAGE, SVG_TAG_PREFIX, XML_LANGUAGE } from '$lib/constants';
|
||||
import { SVG } from '$lib/constants';
|
||||
|
||||
/**
|
||||
* Converts svg code blocks to <pre class="svg-block"> for client-side rendering.
|
||||
@@ -7,7 +7,7 @@ import { SVG_BLOCK_CLASS, SVG_LANGUAGE, SVG_TAG_PREFIX, XML_LANGUAGE } from '$li
|
||||
* svg inside an xml fence.
|
||||
*/
|
||||
export const rehypeSvgPre = createPreTransform(
|
||||
[SVG_LANGUAGE, XML_LANGUAGE],
|
||||
SVG_BLOCK_CLASS,
|
||||
(text) => text.startsWith(SVG_TAG_PREFIX)
|
||||
[SVG.LANGUAGE, SVG.XML_LANGUAGE],
|
||||
SVG.BLOCK_CLASS,
|
||||
(text) => text.startsWith(SVG.TAG_PREFIX)
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import MermaidPreviewControls from './MermaidPreviewControls.svelte';
|
||||
import { SVG_DIALOG_SHADOW_STYLE } from '$lib/constants';
|
||||
import { SVG } from '$lib/constants';
|
||||
import { mountSvgShadow } from '$lib/utils/svg-shadow';
|
||||
|
||||
interface Props {
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
// Re-mount on every svgHtml change so a live streaming svg keeps rendering while zoomed
|
||||
$effect(() => {
|
||||
if (svgHost) mountSvgShadow(svgHost, svgHtml, SVG_DIALOG_SHADOW_STYLE);
|
||||
if (svgHost) mountSvgShadow(svgHost, svgHtml, SVG.DIALOG_SHADOW_STYLE);
|
||||
});
|
||||
|
||||
// Zoom and pan state
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import RotateCcwIcon from '@lucide/svelte/icons/rotate-ccw';
|
||||
import ZoomInIcon from '@lucide/svelte/icons/zoom-in';
|
||||
import ZoomOutIcon from '@lucide/svelte/icons/zoom-out';
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants';
|
||||
|
||||
interface Props {
|
||||
scale: number;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { SYNTAX_CODE_SCROLL_AT_BOTTOM_THRESHOLD_PX } from '$lib/constants/auto-scroll';
|
||||
import { SYNTAX_CODE_SCROLL_AT_BOTTOM_THRESHOLD_PX } from '$lib/constants';
|
||||
import { ColorMode } from '$lib/enums';
|
||||
import { highlightCode } from '$lib/utils';
|
||||
import githubLightCss from 'highlight.js/styles/github.css?inline';
|
||||
|
||||
Reference in New Issue
Block a user