@open-mercato/ui 0.6.6-develop.6339.1.193c6c7c71 → 0.6.6-develop.6344.1.c4b17c07c4
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.
|
@@ -13,7 +13,7 @@ function normalizeTagLabels(labels) {
|
|
|
13
13
|
new Set(
|
|
14
14
|
labels.map((label) => label.trim().toLowerCase()).filter((label) => label.length > 0)
|
|
15
15
|
)
|
|
16
|
-
).sort();
|
|
16
|
+
).sort((a, b) => a.localeCompare(b));
|
|
17
17
|
}
|
|
18
18
|
function buildTagLabelKey(labels) {
|
|
19
19
|
return normalizeTagLabels(labels).join("\0");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/detail/TagsSection.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Pencil, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { TagsInput } from '@open-mercato/ui/backend/inputs/TagsInput'\nimport { DataLoader } from '@open-mercato/ui/primitives/DataLoader'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\n\nexport type TagOption = {\n id: string\n label: string\n color?: string | null\n}\n\nexport type TagsSectionLabels = {\n loading: string\n placeholder: string\n empty: string\n loadError: string\n createError: string\n updateError: string\n labelRequired: string\n saveShortcut: string\n cancelShortcut: string\n edit?: string\n cancel?: string\n success?: string\n saving?: string\n autoSaveHint?: string\n}\n\nexport type TagsSectionController = {\n flush: () => Promise<void>\n}\n\nexport type TagsSectionProps = {\n title: string\n tags: TagOption[]\n onChange?: (next: TagOption[]) => void\n isSubmitting?: boolean\n canEdit?: boolean\n autoSave?: boolean\n controllerRef?: React.MutableRefObject<TagsSectionController | null>\n loadOptions: (query?: string) => Promise<TagOption[]>\n createTag: (label: string) => Promise<TagOption>\n onSave: (params: {\n next: TagOption[]\n added: TagOption[]\n removed: TagOption[]\n }) => Promise<void>\n labels: TagsSectionLabels\n}\n\nfunction normalizeTagLabels(labels: string[]): string[] {\n return Array.from(\n new Set(\n labels\n .map((label) => label.trim().toLowerCase())\n .filter((label) => label.length > 0),\n ),\n ).sort()\n}\n\nfunction buildTagLabelKey(labels: string[]): string {\n return normalizeTagLabels(labels).join('\\0')\n}\n\nfunction TagsSectionImpl({\n title,\n tags,\n onChange,\n isSubmitting = false,\n canEdit = true,\n autoSave = false,\n loadOptions,\n createTag,\n onSave,\n labels,\n controllerRef,\n}: TagsSectionProps) {\n const [editing, setEditing] = React.useState(autoSave)\n const [draft, setDraft] = React.useState<string[]>([])\n const [saving, setSaving] = React.useState(false)\n const [, setOptions] = React.useState<Map<string, TagOption>>(() => new Map())\n const [loadingOptions, setLoadingOptions] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const draftRef = React.useRef<string[]>([])\n const savedTagsRef = React.useRef<TagOption[]>(tags)\n const optionsRef = React.useRef<Map<string, TagOption>>(new Map())\n const saveTaskRef = React.useRef<Promise<void> | null>(null)\n React.useEffect(() => {\n savedTagsRef.current = tags\n setOptions((prev) => {\n const next = new Map(prev)\n for (const tag of tags) {\n next.set(tag.label.toLowerCase(), tag)\n }\n optionsRef.current = next\n return next\n })\n }, [tags])\n\n React.useEffect(() => {\n draftRef.current = draft\n }, [draft])\n\n const syncFetchedOptions = React.useCallback((fetched: TagOption[]) => {\n if (!fetched.length) return\n setOptions((prev) => {\n const next = new Map(prev)\n for (const tag of fetched) {\n next.set(tag.label.toLowerCase(), tag)\n }\n optionsRef.current = next\n return next\n })\n }, [])\n\n const hasPendingDraftChanges = React.useCallback(() => {\n if (!autoSave || !autoSaveUserEditedRef.current) return false\n const draftLabels = normalizeTagLabels(draftRef.current)\n const savedLabels = normalizeTagLabels(savedTagsRef.current.map((tag) => tag.label))\n if (draftLabels.length !== savedLabels.length) return true\n return draftLabels.some((label, index) => label !== savedLabels[index])\n }, [autoSave])\n\n const loadSuggestions = React.useCallback(\n async (query?: string) => {\n try {\n const fetched = await loadOptions(query)\n syncFetchedOptions(fetched)\n return fetched.map((tag) => tag.label)\n } catch (err) {\n console.error('tags.section.loadSuggestions', err)\n return []\n }\n },\n [loadOptions, syncFetchedOptions],\n )\n\n const startEditing = React.useCallback(async () => {\n if (editing || isSubmitting || !canEdit) return\n setError(null)\n setDraft(tags.map((tag) => tag.label))\n setEditing(true)\n setLoadingOptions(true)\n try {\n const fetched = await loadOptions()\n syncFetchedOptions(fetched)\n } catch (err) {\n const message = err instanceof Error ? err.message : labels.loadError\n setError(message)\n flash(message, 'error')\n } finally {\n setLoadingOptions(false)\n }\n }, [canEdit, editing, isSubmitting, labels.loadError, loadOptions, syncFetchedOptions, tags])\n\n const cancelEditing = React.useCallback(() => {\n setEditing(false)\n setDraft([])\n setError(null)\n }, [])\n\n const ensureTagOption = React.useCallback(\n async (label: string): Promise<TagOption> => {\n const normalized = label.trim()\n if (!normalized.length) {\n throw new Error(labels.labelRequired)\n }\n const existing = optionsRef.current.get(normalized.toLowerCase())\n if (existing) return existing\n try {\n const created = await createTag(normalized)\n setOptions((prev) => {\n const next = new Map(prev)\n next.set(created.label.toLowerCase(), created)\n optionsRef.current = next\n return next\n })\n return created\n } catch (err) {\n const message = err instanceof Error ? err.message : labels.createError\n throw new Error(message)\n }\n },\n [createTag, labels.createError, labels.labelRequired],\n )\n\n const handleSave = React.useCallback(async () => {\n if (saveTaskRef.current) {\n await saveTaskRef.current\n if (!autoSave || !hasPendingDraftChanges()) return\n }\n if (autoSave && !hasPendingDraftChanges()) {\n autoSaveUserEditedRef.current = false\n return\n }\n\n const trimmed = draftRef.current.map((label) => label.trim()).filter((label) => label.length > 0)\n const uniqueLabels = Array.from(new Set(trimmed.map((label) => label.toLowerCase())))\n\n let shouldContinueAutoSave = false\n\n const task = (async () => {\n const currentTags = savedTagsRef.current\n const currentIds = new Set(currentTags.map((tag) => tag.id))\n const finalTagOptions: TagOption[] = []\n const submittedDraftKey = buildTagLabelKey(trimmed)\n\n setSaving(true)\n setError(null)\n try {\n for (const normalized of uniqueLabels) {\n const existing = optionsRef.current.get(normalized)\n if (existing) {\n finalTagOptions.push(existing)\n continue\n }\n const matchingLabel = trimmed.find((label) => label.toLowerCase() === normalized) ?? normalized\n const created = await ensureTagOption(matchingLabel)\n finalTagOptions.push(created)\n }\n\n const finalIds = new Set(finalTagOptions.map((tag) => tag.id))\n const added = finalTagOptions.filter((tag) => !currentIds.has(tag.id))\n const removed = currentTags.filter((tag) => !finalIds.has(tag.id))\n\n if (added.length > 0 || removed.length > 0) {\n await onSave({ next: finalTagOptions, added, removed })\n }\n\n savedTagsRef.current = finalTagOptions\n onChange?.(finalTagOptions)\n if (autoSave) {\n const latestDraftKey = buildTagLabelKey(draftRef.current)\n if (latestDraftKey === submittedDraftKey) {\n autoSaveUserEditedRef.current = false\n setDraft(finalTagOptions.map((tag) => tag.label))\n } else {\n shouldContinueAutoSave = true\n }\n } else {\n setEditing(false)\n setDraft([])\n }\n if (labels.success && (added.length > 0 || removed.length > 0)) flash(labels.success, 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : labels.updateError\n setError(message)\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n })()\n\n saveTaskRef.current = task\n try {\n await task\n } finally {\n saveTaskRef.current = null\n }\n if (shouldContinueAutoSave) {\n void handleSave()\n }\n }, [autoSave, ensureTagOption, hasPendingDraftChanges, labels.success, labels.updateError, onChange, onSave])\n\n const activeTags = editing ? draft : tags.map((tag) => tag.label)\n\n const handleEditingKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!editing) return\n if (event.key === 'Escape') {\n event.preventDefault()\n cancelEditing()\n return\n }\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (saving || isSubmitting) return\n void handleSave()\n }\n },\n [cancelEditing, editing, handleSave, isSubmitting, saving],\n )\n\n const autoSaveInitializedRef = React.useRef(false)\n\n React.useEffect(() => {\n if (!autoSave || autoSaveInitializedRef.current) return\n autoSaveInitializedRef.current = true\n setEditing(true)\n setDraft(tags.map((tag) => tag.label))\n let cancelled = false\n loadOptions().then((fetched) => {\n if (cancelled) return\n syncFetchedOptions(fetched)\n }).catch(() => {})\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoSave])\n\n const autoSaveUserEditedRef = React.useRef(false)\n const originalOnChange = React.useCallback(\n (values: string[]) => {\n autoSaveUserEditedRef.current = true\n setDraft(values)\n },\n [],\n )\n\n React.useEffect(() => {\n if (!autoSave || !editing || !autoSaveUserEditedRef.current) return\n if (!hasPendingDraftChanges()) return\n void handleSave()\n }, [autoSave, draft, editing, handleSave, hasPendingDraftChanges])\n\n const flush = React.useCallback(async () => {\n if (saveTaskRef.current) {\n await saveTaskRef.current\n if (!autoSave || !editing || !hasPendingDraftChanges()) return\n }\n if (!autoSave || !editing || !hasPendingDraftChanges()) return\n await handleSave()\n }, [autoSave, editing, handleSave, hasPendingDraftChanges])\n\n React.useEffect(() => {\n if (!controllerRef) return\n controllerRef.current = { flush }\n return () => {\n if (controllerRef.current?.flush === flush) {\n controllerRef.current = null\n }\n }\n }, [controllerRef, flush])\n\n const disableInteraction = isSubmitting || !canEdit\n const autoSaveStatusLabel = saving\n ? (labels.saving ?? 'Saving\u2026')\n : (labels.autoSaveHint ?? null)\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between group\">\n <h2 className=\"text-sm font-semibold\">\n {title}\n </h2>\n {autoSave ? (\n autoSaveStatusLabel ? <p className=\"text-xs text-muted-foreground\">{autoSaveStatusLabel}</p> : null\n ) : (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={editing ? cancelEditing : startEditing}\n disabled={disableInteraction || saving}\n className={\n editing\n ? 'opacity-100 transition-opacity duration-150'\n : 'opacity-100 md:opacity-0 transition-opacity duration-150 md:group-hover:opacity-100 focus-visible:opacity-100'\n }\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n <span className=\"sr-only\">\n {editing ? labels.cancel ?? 'Cancel' : labels.edit ?? 'Edit'}\n </span>\n </Button>\n )}\n </div>\n\n {editing ? (\n <div className=\"rounded-lg border bg-card p-4 space-y-3\">\n <DataLoader\n isLoading={loadingOptions}\n loadingMessage={labels.loading}\n spinnerSize=\"sm\"\n >\n <div className=\"space-y-3\" onKeyDown={handleEditingKeyDown}>\n <TagsInput\n value={activeTags}\n onChange={autoSave ? originalOnChange : (values) => setDraft(values)}\n placeholder={labels.placeholder}\n loadSuggestions={loadSuggestions}\n autoFocus={!autoSave}\n />\n {error ? <p className=\"text-xs text-red-600\">{error}</p> : null}\n {autoSave ? null : (\n <div className=\"flex items-center gap-2 mt-3 mb-2\">\n <Button type=\"button\" size=\"sm\" onClick={handleSave} disabled={saving || isSubmitting}>\n {saving ? (\n <span className=\"mr-2 h-4 w-4 animate-spin rounded-full border border-background border-t-primary\" />\n ) : null}\n {labels.saveShortcut}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={cancelEditing}\n disabled={saving || isSubmitting}\n >\n {labels.cancelShortcut}\n </Button>\n </div>\n )}\n </div>\n </DataLoader>\n </div>\n ) : (\n <div\n className=\"group/tags relative rounded-lg border bg-muted/30 p-4 transition-colors hover:border-primary/40 focus-visible:border-primary focus-visible:outline-none\"\n role={disableInteraction ? undefined : 'button'}\n tabIndex={disableInteraction ? -1 : 0}\n onClick={disableInteraction ? undefined : startEditing}\n onKeyDown={(event) => {\n if (disableInteraction) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n void startEditing()\n }\n }}\n >\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute right-3 top-3 text-muted-foreground opacity-0 transition-opacity duration-150 group-hover/tags:opacity-100 group-focus-within/tags:opacity-100\"\n >\n <Pencil className=\"h-4 w-4\" />\n </span>\n {tags.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {labels.empty}\n </p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {tags.map((tag) => (\n <span\n key={tag.id}\n className=\"inline-flex items-center rounded-full border px-3 py-1 text-xs font-medium\"\n style={tag.color ? { borderColor: tag.color, color: tag.color } : undefined}\n >\n {tag.label}\n </span>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n\nexport function TagsSection(props: TagsSectionProps) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'TagsSection')\n const Resolved = useRegisteredComponent<TagsSectionProps>(\n handle,\n TagsSectionImpl as React.ComponentType<TagsSectionProps>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n\nexport default TagsSection\n"],
|
|
5
|
-
"mappings": ";AA4VQ,cAME,YANF;AA1VR,YAAY,WAAW;AACvB,SAAS,QAAQ,SAAS;AAC1B,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,mCAAmC;AAC5C,SAAS,8BAA8B;AA+CvC,SAAS,mBAAmB,QAA4B;AACtD,SAAO,MAAM;AAAA,IACX,IAAI;AAAA,MACF,OACG,IAAI,CAAC,UAAU,MAAM,KAAK,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAAA,IACvC;AAAA,EACF,EAAE,KAAK;
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Pencil, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { TagsInput } from '@open-mercato/ui/backend/inputs/TagsInput'\nimport { DataLoader } from '@open-mercato/ui/primitives/DataLoader'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ComponentReplacementHandles } from '@open-mercato/shared/modules/widgets/component-registry'\nimport { useRegisteredComponent } from '../injection/useRegisteredComponent'\n\nexport type TagOption = {\n id: string\n label: string\n color?: string | null\n}\n\nexport type TagsSectionLabels = {\n loading: string\n placeholder: string\n empty: string\n loadError: string\n createError: string\n updateError: string\n labelRequired: string\n saveShortcut: string\n cancelShortcut: string\n edit?: string\n cancel?: string\n success?: string\n saving?: string\n autoSaveHint?: string\n}\n\nexport type TagsSectionController = {\n flush: () => Promise<void>\n}\n\nexport type TagsSectionProps = {\n title: string\n tags: TagOption[]\n onChange?: (next: TagOption[]) => void\n isSubmitting?: boolean\n canEdit?: boolean\n autoSave?: boolean\n controllerRef?: React.MutableRefObject<TagsSectionController | null>\n loadOptions: (query?: string) => Promise<TagOption[]>\n createTag: (label: string) => Promise<TagOption>\n onSave: (params: {\n next: TagOption[]\n added: TagOption[]\n removed: TagOption[]\n }) => Promise<void>\n labels: TagsSectionLabels\n}\n\nfunction normalizeTagLabels(labels: string[]): string[] {\n return Array.from(\n new Set(\n labels\n .map((label) => label.trim().toLowerCase())\n .filter((label) => label.length > 0),\n ),\n ).sort((a, b) => a.localeCompare(b))\n}\n\nfunction buildTagLabelKey(labels: string[]): string {\n return normalizeTagLabels(labels).join('\\0')\n}\n\nfunction TagsSectionImpl({\n title,\n tags,\n onChange,\n isSubmitting = false,\n canEdit = true,\n autoSave = false,\n loadOptions,\n createTag,\n onSave,\n labels,\n controllerRef,\n}: TagsSectionProps) {\n const [editing, setEditing] = React.useState(autoSave)\n const [draft, setDraft] = React.useState<string[]>([])\n const [saving, setSaving] = React.useState(false)\n const [, setOptions] = React.useState<Map<string, TagOption>>(() => new Map())\n const [loadingOptions, setLoadingOptions] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const draftRef = React.useRef<string[]>([])\n const savedTagsRef = React.useRef<TagOption[]>(tags)\n const optionsRef = React.useRef<Map<string, TagOption>>(new Map())\n const saveTaskRef = React.useRef<Promise<void> | null>(null)\n React.useEffect(() => {\n savedTagsRef.current = tags\n setOptions((prev) => {\n const next = new Map(prev)\n for (const tag of tags) {\n next.set(tag.label.toLowerCase(), tag)\n }\n optionsRef.current = next\n return next\n })\n }, [tags])\n\n React.useEffect(() => {\n draftRef.current = draft\n }, [draft])\n\n const syncFetchedOptions = React.useCallback((fetched: TagOption[]) => {\n if (!fetched.length) return\n setOptions((prev) => {\n const next = new Map(prev)\n for (const tag of fetched) {\n next.set(tag.label.toLowerCase(), tag)\n }\n optionsRef.current = next\n return next\n })\n }, [])\n\n const hasPendingDraftChanges = React.useCallback(() => {\n if (!autoSave || !autoSaveUserEditedRef.current) return false\n const draftLabels = normalizeTagLabels(draftRef.current)\n const savedLabels = normalizeTagLabels(savedTagsRef.current.map((tag) => tag.label))\n if (draftLabels.length !== savedLabels.length) return true\n return draftLabels.some((label, index) => label !== savedLabels[index])\n }, [autoSave])\n\n const loadSuggestions = React.useCallback(\n async (query?: string) => {\n try {\n const fetched = await loadOptions(query)\n syncFetchedOptions(fetched)\n return fetched.map((tag) => tag.label)\n } catch (err) {\n console.error('tags.section.loadSuggestions', err)\n return []\n }\n },\n [loadOptions, syncFetchedOptions],\n )\n\n const startEditing = React.useCallback(async () => {\n if (editing || isSubmitting || !canEdit) return\n setError(null)\n setDraft(tags.map((tag) => tag.label))\n setEditing(true)\n setLoadingOptions(true)\n try {\n const fetched = await loadOptions()\n syncFetchedOptions(fetched)\n } catch (err) {\n const message = err instanceof Error ? err.message : labels.loadError\n setError(message)\n flash(message, 'error')\n } finally {\n setLoadingOptions(false)\n }\n }, [canEdit, editing, isSubmitting, labels.loadError, loadOptions, syncFetchedOptions, tags])\n\n const cancelEditing = React.useCallback(() => {\n setEditing(false)\n setDraft([])\n setError(null)\n }, [])\n\n const ensureTagOption = React.useCallback(\n async (label: string): Promise<TagOption> => {\n const normalized = label.trim()\n if (!normalized.length) {\n throw new Error(labels.labelRequired)\n }\n const existing = optionsRef.current.get(normalized.toLowerCase())\n if (existing) return existing\n try {\n const created = await createTag(normalized)\n setOptions((prev) => {\n const next = new Map(prev)\n next.set(created.label.toLowerCase(), created)\n optionsRef.current = next\n return next\n })\n return created\n } catch (err) {\n const message = err instanceof Error ? err.message : labels.createError\n throw new Error(message)\n }\n },\n [createTag, labels.createError, labels.labelRequired],\n )\n\n const handleSave = React.useCallback(async () => {\n if (saveTaskRef.current) {\n await saveTaskRef.current\n if (!autoSave || !hasPendingDraftChanges()) return\n }\n if (autoSave && !hasPendingDraftChanges()) {\n autoSaveUserEditedRef.current = false\n return\n }\n\n const trimmed = draftRef.current.map((label) => label.trim()).filter((label) => label.length > 0)\n const uniqueLabels = Array.from(new Set(trimmed.map((label) => label.toLowerCase())))\n\n let shouldContinueAutoSave = false\n\n const task = (async () => {\n const currentTags = savedTagsRef.current\n const currentIds = new Set(currentTags.map((tag) => tag.id))\n const finalTagOptions: TagOption[] = []\n const submittedDraftKey = buildTagLabelKey(trimmed)\n\n setSaving(true)\n setError(null)\n try {\n for (const normalized of uniqueLabels) {\n const existing = optionsRef.current.get(normalized)\n if (existing) {\n finalTagOptions.push(existing)\n continue\n }\n const matchingLabel = trimmed.find((label) => label.toLowerCase() === normalized) ?? normalized\n const created = await ensureTagOption(matchingLabel)\n finalTagOptions.push(created)\n }\n\n const finalIds = new Set(finalTagOptions.map((tag) => tag.id))\n const added = finalTagOptions.filter((tag) => !currentIds.has(tag.id))\n const removed = currentTags.filter((tag) => !finalIds.has(tag.id))\n\n if (added.length > 0 || removed.length > 0) {\n await onSave({ next: finalTagOptions, added, removed })\n }\n\n savedTagsRef.current = finalTagOptions\n onChange?.(finalTagOptions)\n if (autoSave) {\n const latestDraftKey = buildTagLabelKey(draftRef.current)\n if (latestDraftKey === submittedDraftKey) {\n autoSaveUserEditedRef.current = false\n setDraft(finalTagOptions.map((tag) => tag.label))\n } else {\n shouldContinueAutoSave = true\n }\n } else {\n setEditing(false)\n setDraft([])\n }\n if (labels.success && (added.length > 0 || removed.length > 0)) flash(labels.success, 'success')\n } catch (err) {\n const message = err instanceof Error ? err.message : labels.updateError\n setError(message)\n flash(message, 'error')\n } finally {\n setSaving(false)\n }\n })()\n\n saveTaskRef.current = task\n try {\n await task\n } finally {\n saveTaskRef.current = null\n }\n if (shouldContinueAutoSave) {\n void handleSave()\n }\n }, [autoSave, ensureTagOption, hasPendingDraftChanges, labels.success, labels.updateError, onChange, onSave])\n\n const activeTags = editing ? draft : tags.map((tag) => tag.label)\n\n const handleEditingKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!editing) return\n if (event.key === 'Escape') {\n event.preventDefault()\n cancelEditing()\n return\n }\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (saving || isSubmitting) return\n void handleSave()\n }\n },\n [cancelEditing, editing, handleSave, isSubmitting, saving],\n )\n\n const autoSaveInitializedRef = React.useRef(false)\n\n React.useEffect(() => {\n if (!autoSave || autoSaveInitializedRef.current) return\n autoSaveInitializedRef.current = true\n setEditing(true)\n setDraft(tags.map((tag) => tag.label))\n let cancelled = false\n loadOptions().then((fetched) => {\n if (cancelled) return\n syncFetchedOptions(fetched)\n }).catch(() => {})\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoSave])\n\n const autoSaveUserEditedRef = React.useRef(false)\n const originalOnChange = React.useCallback(\n (values: string[]) => {\n autoSaveUserEditedRef.current = true\n setDraft(values)\n },\n [],\n )\n\n React.useEffect(() => {\n if (!autoSave || !editing || !autoSaveUserEditedRef.current) return\n if (!hasPendingDraftChanges()) return\n void handleSave()\n }, [autoSave, draft, editing, handleSave, hasPendingDraftChanges])\n\n const flush = React.useCallback(async () => {\n if (saveTaskRef.current) {\n await saveTaskRef.current\n if (!autoSave || !editing || !hasPendingDraftChanges()) return\n }\n if (!autoSave || !editing || !hasPendingDraftChanges()) return\n await handleSave()\n }, [autoSave, editing, handleSave, hasPendingDraftChanges])\n\n React.useEffect(() => {\n if (!controllerRef) return\n controllerRef.current = { flush }\n return () => {\n if (controllerRef.current?.flush === flush) {\n controllerRef.current = null\n }\n }\n }, [controllerRef, flush])\n\n const disableInteraction = isSubmitting || !canEdit\n const autoSaveStatusLabel = saving\n ? (labels.saving ?? 'Saving\u2026')\n : (labels.autoSaveHint ?? null)\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between group\">\n <h2 className=\"text-sm font-semibold\">\n {title}\n </h2>\n {autoSave ? (\n autoSaveStatusLabel ? <p className=\"text-xs text-muted-foreground\">{autoSaveStatusLabel}</p> : null\n ) : (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={editing ? cancelEditing : startEditing}\n disabled={disableInteraction || saving}\n className={\n editing\n ? 'opacity-100 transition-opacity duration-150'\n : 'opacity-100 md:opacity-0 transition-opacity duration-150 md:group-hover:opacity-100 focus-visible:opacity-100'\n }\n >\n {editing ? <X className=\"h-4 w-4\" /> : <Pencil className=\"h-4 w-4\" />}\n <span className=\"sr-only\">\n {editing ? labels.cancel ?? 'Cancel' : labels.edit ?? 'Edit'}\n </span>\n </Button>\n )}\n </div>\n\n {editing ? (\n <div className=\"rounded-lg border bg-card p-4 space-y-3\">\n <DataLoader\n isLoading={loadingOptions}\n loadingMessage={labels.loading}\n spinnerSize=\"sm\"\n >\n <div className=\"space-y-3\" onKeyDown={handleEditingKeyDown}>\n <TagsInput\n value={activeTags}\n onChange={autoSave ? originalOnChange : (values) => setDraft(values)}\n placeholder={labels.placeholder}\n loadSuggestions={loadSuggestions}\n autoFocus={!autoSave}\n />\n {error ? <p className=\"text-xs text-red-600\">{error}</p> : null}\n {autoSave ? null : (\n <div className=\"flex items-center gap-2 mt-3 mb-2\">\n <Button type=\"button\" size=\"sm\" onClick={handleSave} disabled={saving || isSubmitting}>\n {saving ? (\n <span className=\"mr-2 h-4 w-4 animate-spin rounded-full border border-background border-t-primary\" />\n ) : null}\n {labels.saveShortcut}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n onClick={cancelEditing}\n disabled={saving || isSubmitting}\n >\n {labels.cancelShortcut}\n </Button>\n </div>\n )}\n </div>\n </DataLoader>\n </div>\n ) : (\n <div\n className=\"group/tags relative rounded-lg border bg-muted/30 p-4 transition-colors hover:border-primary/40 focus-visible:border-primary focus-visible:outline-none\"\n role={disableInteraction ? undefined : 'button'}\n tabIndex={disableInteraction ? -1 : 0}\n onClick={disableInteraction ? undefined : startEditing}\n onKeyDown={(event) => {\n if (disableInteraction) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n void startEditing()\n }\n }}\n >\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute right-3 top-3 text-muted-foreground opacity-0 transition-opacity duration-150 group-hover/tags:opacity-100 group-focus-within/tags:opacity-100\"\n >\n <Pencil className=\"h-4 w-4\" />\n </span>\n {tags.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {labels.empty}\n </p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {tags.map((tag) => (\n <span\n key={tag.id}\n className=\"inline-flex items-center rounded-full border px-3 py-1 text-xs font-medium\"\n style={tag.color ? { borderColor: tag.color, color: tag.color } : undefined}\n >\n {tag.label}\n </span>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n\nexport function TagsSection(props: TagsSectionProps) {\n const handle = ComponentReplacementHandles.section('ui.detail', 'TagsSection')\n const Resolved = useRegisteredComponent<TagsSectionProps>(\n handle,\n TagsSectionImpl as React.ComponentType<TagsSectionProps>,\n )\n\n return (\n <div data-component-handle={handle}>\n <Resolved {...props} />\n </div>\n )\n}\n\nexport default TagsSection\n"],
|
|
5
|
+
"mappings": ";AA4VQ,cAME,YANF;AA1VR,YAAY,WAAW;AACvB,SAAS,QAAQ,SAAS;AAC1B,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,mCAAmC;AAC5C,SAAS,8BAA8B;AA+CvC,SAAS,mBAAmB,QAA4B;AACtD,SAAO,MAAM;AAAA,IACX,IAAI;AAAA,MACF,OACG,IAAI,CAAC,UAAU,MAAM,KAAK,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAAA,IACvC;AAAA,EACF,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACrC;AAEA,SAAS,iBAAiB,QAA0B;AAClD,SAAO,mBAAmB,MAAM,EAAE,KAAK,IAAI;AAC7C;AAEA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,UAAU;AAAA,EACV,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,QAAQ;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAmB,CAAC,CAAC;AACrD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,EAAE,UAAU,IAAI,MAAM,SAAiC,MAAM,oBAAI,IAAI,CAAC;AAC7E,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,KAAK;AAChE,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAE5D,QAAM,WAAW,MAAM,OAAiB,CAAC,CAAC;AAC1C,QAAM,eAAe,MAAM,OAAoB,IAAI;AACnD,QAAM,aAAa,MAAM,OAA+B,oBAAI,IAAI,CAAC;AACjE,QAAM,cAAc,MAAM,OAA6B,IAAI;AAC3D,QAAM,UAAU,MAAM;AACpB,iBAAa,UAAU;AACvB,eAAW,CAAC,SAAS;AACnB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,iBAAW,OAAO,MAAM;AACtB,aAAK,IAAI,IAAI,MAAM,YAAY,GAAG,GAAG;AAAA,MACvC;AACA,iBAAW,UAAU;AACrB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UAAU,MAAM;AACpB,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,qBAAqB,MAAM,YAAY,CAAC,YAAyB;AACrE,QAAI,CAAC,QAAQ,OAAQ;AACrB,eAAW,CAAC,SAAS;AACnB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,iBAAW,OAAO,SAAS;AACzB,aAAK,IAAI,IAAI,MAAM,YAAY,GAAG,GAAG;AAAA,MACvC;AACA,iBAAW,UAAU;AACrB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,yBAAyB,MAAM,YAAY,MAAM;AACrD,QAAI,CAAC,YAAY,CAAC,sBAAsB,QAAS,QAAO;AACxD,UAAM,cAAc,mBAAmB,SAAS,OAAO;AACvD,UAAM,cAAc,mBAAmB,aAAa,QAAQ,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;AACnF,QAAI,YAAY,WAAW,YAAY,OAAQ,QAAO;AACtD,WAAO,YAAY,KAAK,CAAC,OAAO,UAAU,UAAU,YAAY,KAAK,CAAC;AAAA,EACxE,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,kBAAkB,MAAM;AAAA,IAC5B,OAAO,UAAmB;AACxB,UAAI;AACF,cAAM,UAAU,MAAM,YAAY,KAAK;AACvC,2BAAmB,OAAO;AAC1B,eAAO,QAAQ,IAAI,CAAC,QAAQ,IAAI,KAAK;AAAA,MACvC,SAAS,KAAK;AACZ,gBAAQ,MAAM,gCAAgC,GAAG;AACjD,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC,aAAa,kBAAkB;AAAA,EAClC;AAEA,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,QAAI,WAAW,gBAAgB,CAAC,QAAS;AACzC,aAAS,IAAI;AACb,aAAS,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;AACrC,eAAW,IAAI;AACf,sBAAkB,IAAI;AACtB,QAAI;AACF,YAAM,UAAU,MAAM,YAAY;AAClC,yBAAmB,OAAO;AAAA,IAC5B,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO;AAC5D,eAAS,OAAO;AAChB,YAAM,SAAS,OAAO;AAAA,IACxB,UAAE;AACA,wBAAkB,KAAK;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,SAAS,SAAS,cAAc,OAAO,WAAW,aAAa,oBAAoB,IAAI,CAAC;AAE5F,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,eAAW,KAAK;AAChB,aAAS,CAAC,CAAC;AACX,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,CAAC;AAEL,QAAM,kBAAkB,MAAM;AAAA,IAC5B,OAAO,UAAsC;AAC3C,YAAM,aAAa,MAAM,KAAK;AAC9B,UAAI,CAAC,WAAW,QAAQ;AACtB,cAAM,IAAI,MAAM,OAAO,aAAa;AAAA,MACtC;AACA,YAAM,WAAW,WAAW,QAAQ,IAAI,WAAW,YAAY,CAAC;AAChE,UAAI,SAAU,QAAO;AACrB,UAAI;AACF,cAAM,UAAU,MAAM,UAAU,UAAU;AAC1C,mBAAW,CAAC,SAAS;AACnB,gBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,eAAK,IAAI,QAAQ,MAAM,YAAY,GAAG,OAAO;AAC7C,qBAAW,UAAU;AACrB,iBAAO;AAAA,QACT,CAAC;AACD,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO;AAC5D,cAAM,IAAI,MAAM,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,IACA,CAAC,WAAW,OAAO,aAAa,OAAO,aAAa;AAAA,EACtD;AAEA,QAAM,aAAa,MAAM,YAAY,YAAY;AAC/C,QAAI,YAAY,SAAS;AACvB,YAAM,YAAY;AAClB,UAAI,CAAC,YAAY,CAAC,uBAAuB,EAAG;AAAA,IAC9C;AACA,QAAI,YAAY,CAAC,uBAAuB,GAAG;AACzC,4BAAsB,UAAU;AAChC;AAAA,IACF;AAEA,UAAM,UAAU,SAAS,QAAQ,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAChG,UAAM,eAAe,MAAM,KAAK,IAAI,IAAI,QAAQ,IAAI,CAAC,UAAU,MAAM,YAAY,CAAC,CAAC,CAAC;AAEpF,QAAI,yBAAyB;AAE7B,UAAM,QAAQ,YAAY;AACxB,YAAM,cAAc,aAAa;AACjC,YAAM,aAAa,IAAI,IAAI,YAAY,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;AAC3D,YAAM,kBAA+B,CAAC;AACtC,YAAM,oBAAoB,iBAAiB,OAAO;AAElD,gBAAU,IAAI;AACd,eAAS,IAAI;AACb,UAAI;AACF,mBAAW,cAAc,cAAc;AACrC,gBAAM,WAAW,WAAW,QAAQ,IAAI,UAAU;AAClD,cAAI,UAAU;AACZ,4BAAgB,KAAK,QAAQ;AAC7B;AAAA,UACF;AACA,gBAAM,gBAAgB,QAAQ,KAAK,CAAC,UAAU,MAAM,YAAY,MAAM,UAAU,KAAK;AACrF,gBAAM,UAAU,MAAM,gBAAgB,aAAa;AACnD,0BAAgB,KAAK,OAAO;AAAA,QAC9B;AAEA,cAAM,WAAW,IAAI,IAAI,gBAAgB,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;AAC7D,cAAM,QAAQ,gBAAgB,OAAO,CAAC,QAAQ,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;AACrE,cAAM,UAAU,YAAY,OAAO,CAAC,QAAQ,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;AAEjE,YAAI,MAAM,SAAS,KAAK,QAAQ,SAAS,GAAG;AAC1C,gBAAM,OAAO,EAAE,MAAM,iBAAiB,OAAO,QAAQ,CAAC;AAAA,QACxD;AAEA,qBAAa,UAAU;AACvB,mBAAW,eAAe;AAC1B,YAAI,UAAU;AACZ,gBAAM,iBAAiB,iBAAiB,SAAS,OAAO;AACxD,cAAI,mBAAmB,mBAAmB;AACxC,kCAAsB,UAAU;AAChC,qBAAS,gBAAgB,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;AAAA,UAClD,OAAO;AACL,qCAAyB;AAAA,UAC3B;AAAA,QACF,OAAO;AACL,qBAAW,KAAK;AAChB,mBAAS,CAAC,CAAC;AAAA,QACb;AACA,YAAI,OAAO,YAAY,MAAM,SAAS,KAAK,QAAQ,SAAS,GAAI,OAAM,OAAO,SAAS,SAAS;AAAA,MACjG,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO;AAC5D,iBAAS,OAAO;AAChB,cAAM,SAAS,OAAO;AAAA,MACxB,UAAE;AACA,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF,GAAG;AAEH,gBAAY,UAAU;AACtB,QAAI;AACF,YAAM;AAAA,IACR,UAAE;AACA,kBAAY,UAAU;AAAA,IACxB;AACA,QAAI,wBAAwB;AAC1B,WAAK,WAAW;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,UAAU,iBAAiB,wBAAwB,OAAO,SAAS,OAAO,aAAa,UAAU,MAAM,CAAC;AAE5G,QAAM,aAAa,UAAU,QAAQ,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK;AAEhE,QAAM,uBAAuB,MAAM;AAAA,IACjC,CAAC,UAA+C;AAC9C,UAAI,CAAC,QAAS;AACd,UAAI,MAAM,QAAQ,UAAU;AAC1B,cAAM,eAAe;AACrB,sBAAc;AACd;AAAA,MACF;AACA,WAAK,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,SAAS;AAC7D,cAAM,eAAe;AACrB,YAAI,UAAU,aAAc;AAC5B,aAAK,WAAW;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,eAAe,SAAS,YAAY,cAAc,MAAM;AAAA,EAC3D;AAEA,QAAM,yBAAyB,MAAM,OAAO,KAAK;AAEjD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,YAAY,uBAAuB,QAAS;AACjD,2BAAuB,UAAU;AACjC,eAAW,IAAI;AACf,aAAS,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;AACrC,QAAI,YAAY;AAChB,gBAAY,EAAE,KAAK,CAAC,YAAY;AAC9B,UAAI,UAAW;AACf,yBAAmB,OAAO;AAAA,IAC5B,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACjB,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAElC,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,wBAAwB,MAAM,OAAO,KAAK;AAChD,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,WAAqB;AACpB,4BAAsB,UAAU;AAChC,eAAS,MAAM;AAAA,IACjB;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,YAAY,CAAC,WAAW,CAAC,sBAAsB,QAAS;AAC7D,QAAI,CAAC,uBAAuB,EAAG;AAC/B,SAAK,WAAW;AAAA,EAClB,GAAG,CAAC,UAAU,OAAO,SAAS,YAAY,sBAAsB,CAAC;AAEjE,QAAM,QAAQ,MAAM,YAAY,YAAY;AAC1C,QAAI,YAAY,SAAS;AACvB,YAAM,YAAY;AAClB,UAAI,CAAC,YAAY,CAAC,WAAW,CAAC,uBAAuB,EAAG;AAAA,IAC1D;AACA,QAAI,CAAC,YAAY,CAAC,WAAW,CAAC,uBAAuB,EAAG;AACxD,UAAM,WAAW;AAAA,EACnB,GAAG,CAAC,UAAU,SAAS,YAAY,sBAAsB,CAAC;AAE1D,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,cAAe;AACpB,kBAAc,UAAU,EAAE,MAAM;AAChC,WAAO,MAAM;AACX,UAAI,cAAc,SAAS,UAAU,OAAO;AAC1C,sBAAc,UAAU;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,eAAe,KAAK,CAAC;AAEzB,QAAM,qBAAqB,gBAAgB,CAAC;AAC5C,QAAM,sBAAsB,SACvB,OAAO,UAAU,iBACjB,OAAO,gBAAgB;AAE5B,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,2CACb;AAAA,0BAAC,QAAG,WAAU,yBACX,iBACH;AAAA,MACC,WACC,sBAAsB,oBAAC,OAAE,WAAU,iCAAiC,+BAAoB,IAAO,OAE/F;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS,UAAU,gBAAgB;AAAA,UACnC,UAAU,sBAAsB;AAAA,UAChC,WACE,UACI,gDACA;AAAA,UAGL;AAAA,sBAAU,oBAAC,KAAE,WAAU,WAAU,IAAK,oBAAC,UAAO,WAAU,WAAU;AAAA,YACnE,oBAAC,UAAK,WAAU,WACb,oBAAU,OAAO,UAAU,WAAW,OAAO,QAAQ,QACxD;AAAA;AAAA;AAAA,MACF;AAAA,OAEJ;AAAA,IAEC,UACC,oBAAC,SAAI,WAAU,2CACb;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,aAAY;AAAA,QAEZ,+BAAC,SAAI,WAAU,aAAY,WAAW,sBACpC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,WAAW,mBAAmB,CAAC,WAAW,SAAS,MAAM;AAAA,cACnE,aAAa,OAAO;AAAA,cACpB;AAAA,cACA,WAAW,CAAC;AAAA;AAAA,UACd;AAAA,UACC,QAAQ,oBAAC,OAAE,WAAU,wBAAwB,iBAAM,IAAO;AAAA,UAC1D,WAAW,OACV,qBAAC,SAAI,WAAU,qCACb;AAAA,iCAAC,UAAO,MAAK,UAAS,MAAK,MAAK,SAAS,YAAY,UAAU,UAAU,cACtE;AAAA,uBACC,oBAAC,UAAK,WAAU,oFAAmF,IACjG;AAAA,cACH,OAAO;AAAA,eACV;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,SAAS;AAAA,gBACT,UAAU,UAAU;AAAA,gBAEnB,iBAAO;AAAA;AAAA,YACV;AAAA,aACF;AAAA,WAEJ;AAAA;AAAA,IACF,GACF,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAM,qBAAqB,SAAY;AAAA,QACvC,UAAU,qBAAqB,KAAK;AAAA,QACpC,SAAS,qBAAqB,SAAY;AAAA,QAC1C,WAAW,CAAC,UAAU;AACpB,cAAI,mBAAoB;AACxB,cAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,kBAAM,eAAe;AACrB,iBAAK,aAAa;AAAA,UACpB;AAAA,QACF;AAAA,QAEA;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,eAAY;AAAA,cACZ,WAAU;AAAA,cAEV,8BAAC,UAAO,WAAU,WAAU;AAAA;AAAA,UAC9B;AAAA,UACC,KAAK,WAAW,IACf,oBAAC,OAAE,WAAU,iCACV,iBAAO,OACV,IAEA,oBAAC,SAAI,WAAU,wBACZ,eAAK,IAAI,CAAC,QACT;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cACV,OAAO,IAAI,QAAQ,EAAE,aAAa,IAAI,OAAO,OAAO,IAAI,MAAM,IAAI;AAAA,cAEjE,cAAI;AAAA;AAAA,YAJA,IAAI;AAAA,UAKX,CACD,GACH;AAAA;AAAA;AAAA,IAEJ;AAAA,KAEJ;AAEJ;AAEO,SAAS,YAAY,OAAyB;AACnD,QAAM,SAAS,4BAA4B,QAAQ,aAAa,aAAa;AAC7E,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,SAAI,yBAAuB,QAC1B,8BAAC,YAAU,GAAG,OAAO,GACvB;AAEJ;AAEA,IAAO,sBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -155,13 +155,13 @@
|
|
|
155
155
|
"remark-gfm": "^4.0.1"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
158
|
+
"@open-mercato/shared": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
159
159
|
"react": ">=18.0.0",
|
|
160
160
|
"react-dom": ">=18.0.0",
|
|
161
161
|
"react-is": ">=18.0.0"
|
|
162
162
|
},
|
|
163
163
|
"devDependencies": {
|
|
164
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
164
|
+
"@open-mercato/shared": "0.6.6-develop.6344.1.c4b17c07c4",
|
|
165
165
|
"@testing-library/dom": "^10.4.1",
|
|
166
166
|
"@testing-library/jest-dom": "^6.9.1",
|
|
167
167
|
"@testing-library/react": "^16.3.1",
|
package/src/backend/CrudForm.tsx
CHANGED
|
@@ -555,7 +555,7 @@ function normalizeDirtySnapshotValue(value: unknown): unknown {
|
|
|
555
555
|
|
|
556
556
|
const normalized: Record<string, unknown> = {}
|
|
557
557
|
const record = value as Record<string, unknown>
|
|
558
|
-
for (const key of Object.keys(record).sort()) {
|
|
558
|
+
for (const key of Object.keys(record).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))) {
|
|
559
559
|
const nextValue = normalizeDirtySnapshotValue(record[key])
|
|
560
560
|
if (nextValue !== undefined) normalized[key] = nextValue
|
|
561
561
|
}
|
|
@@ -61,7 +61,7 @@ function normalizeTagLabels(labels: string[]): string[] {
|
|
|
61
61
|
.map((label) => label.trim().toLowerCase())
|
|
62
62
|
.filter((label) => label.length > 0),
|
|
63
63
|
),
|
|
64
|
-
).sort()
|
|
64
|
+
).sort((a, b) => a.localeCompare(b))
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function buildTagLabelKey(labels: string[]): string {
|