@ynput/ayon-frontend-shared 0.3.14 → 0.3.16
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/dist/shared/src/components/AttributeEditor/AttributeEditor.cjs.js +1 -1
- package/dist/shared/src/components/AttributeEditor/AttributeEditor.cjs.js.map +1 -1
- package/dist/shared/src/components/AttributeEditor/AttributeEditor.es.js +99 -90
- package/dist/shared/src/components/AttributeEditor/AttributeEditor.es.js.map +1 -1
- package/dist/shared/src/components/FileThumbnail/FileThumbnail.cjs.js +6 -6
- package/dist/shared/src/components/FileThumbnail/FileThumbnail.cjs.js.map +1 -1
- package/dist/shared/src/components/FileThumbnail/FileThumbnail.es.js +54 -26
- package/dist/shared/src/components/FileThumbnail/FileThumbnail.es.js.map +1 -1
- package/dist/types/components/AttributeEditor/AttributeEditor.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("../../../../_virtual/jsx-runtime.cjs.js"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("../../../../_virtual/jsx-runtime.cjs.js"),O=require("react"),l=require("@ynput/ayon-react-components"),S=require("lodash"),T=require("./components/MinMaxField.cjs.js"),q=require("../EnumEditor/EnumEditor.cjs.js");require("../EnumEditor/EnumEditor.styled.cjs.js");const M=[{value:"project",label:"Project"},{value:"folder",label:"Folder"},{value:"task",label:"Task"},{value:"product",label:"Product"},{value:"version",label:"Version"},{value:"representation",label:"Representation"},{value:"user",label:"User"},{value:"list",label:"List"}],_=[{value:"description",scope:null},{value:"example",scope:null},{value:"default",scope:["project"]},{value:"inherit",scope:["project","folder","task","product","version","representation","user"]}],R={string:{value:"string",label:"String",fields:["minLength","maxLength","enum","regex"]},integer:{value:"integer",label:"Integer",fields:["ge","gt","le","lt"]},float:{value:"float",label:"Decimal number",fields:["ge","gt","le","lt"]},list_of_strings:{value:"list_of_strings",label:"List Of Strings",fields:["minItems","maxItems","enum"]},boolean:{value:"boolean",label:"Boolean",fields:[],exclude:["example"]},datetime:{value:"datetime",label:"Datetime",fields:[]}},P={name:"",scope:["folder","task"],builtin:!1,position:0,data:{type:"string",title:"",description:"",example:"",default:void 0,enum:void 0,minLength:void 0,maxLength:void 0,regex:"",minItems:void 0,maxItems:void 0,ge:void 0,gt:void 0,le:void 0,lt:void 0}},A=(c,u)=>{const r=JSON.parse(JSON.stringify(P)),x=["name"];Object.keys(r).forEach(i=>{!x.includes(i)&&c.includes(i)&&i!=="data"&&delete r[i]});const j=["title"];return r.data&&Object.keys(r.data).forEach(i=>{!j.includes(i)&&c.includes(i)&&delete r.data[i]}),u&&Object.keys(u).forEach(i=>{const p=i;p==="data"&&u.data&&r.data?r.data={...r.data,...u.data}:u[p]!==void 0&&(r[p]=u[p])}),r},B=({attribute:c,defaultData:u,existingNames:r,error:x="",isUpdating:j,excludes:i=[],onHide:p,onEdit:D,onDelete:I})=>{const F=u?{...u,name:u.name||(u.data?.title?S.camelCase(u.data.title):void 0)}:void 0,[t,b]=O.useState(c||A(i,{position:r.length,...F}));O.useEffect(()=>{c&&b(c)},[c]);const g=!c,y=(e,a)=>{b(s=>s&&{...s,[e]:a})},m=(e,a)=>{b(s=>{if(!s||!s.data)return s;const o={...s.data,[e]:a};return{...s,data:o}})};let v="";t&&g&&(r.includes(t.name)?v="This attribute already exists":t.name.match("^[a-zA-Z_]{2,64}$")||(x="Invalid attribute name"));const C=()=>{t&&D(t)},L=n.jsxRuntimeExports.jsxs("div",{style:{display:"flex",width:"100%",flexDirection:"row"},children:[I&&c&&n.jsxRuntimeExports.jsx(l.Button,{variant:"danger",label:"Delete attribute",icon:"delete",disabled:j,onClick:I}),n.jsxRuntimeExports.jsx(l.Spacer,{}),n.jsxRuntimeExports.jsx(l.SaveButton,{label:g?"Create Attribute":"Save Attribute",icon:"check",disabled:!!v||!t,active:!v&&!!t,saving:j,onClick:C})]});let f=[];if(_.forEach(e=>{(!e?.scope||e?.scope?.some(a=>t?.scope?.includes(a)))&&f.push(e.value)}),t?.data.type&&R[t.data.type]){const e=R[t.data.type];f=[...f,...e.fields].filter(a=>!e.exclude?.includes(a))}const w={enum:(e=[],a)=>n.jsxRuntimeExports.jsx(q.EnumEditor,{values:e,onChange:s=>{a(s?.length?s:void 0)}}),inherit:(e,a)=>n.jsxRuntimeExports.jsx(l.InputSwitch,{checked:e,onChange:s=>a(s.target.checked)}),booleanDefault:(e,a)=>n.jsxRuntimeExports.jsx(l.InputSwitch,{checked:e,onChange:s=>a(s.target.checked)})},N=e=>{const a=e.target.value;m("title",a),g&&y("name",S.camelCase(a))};return n.jsxRuntimeExports.jsx(l.Dialog,{header:t?.data?.title||t?.name||"New attribute",footer:L,onClose:p,isOpen:!0,style:{width:700,zIndex:999},size:"full",enableBackdropClose:!1,onKeyDown:e=>{e.key==="Enter"&&(e.metaKey||e.ctrlKey)&&(e.preventDefault(),C())},children:t&&n.jsxRuntimeExports.jsxs(l.FormLayout,{children:[!i.includes("title")&&n.jsxRuntimeExports.jsx(l.FormRow,{label:"Title",children:n.jsxRuntimeExports.jsx(l.InputText,{value:t?.data.title,onChange:N,autoFocus:!0})},"title"),!i.includes("name")&&n.jsxRuntimeExports.jsx(l.FormRow,{label:"Name",children:n.jsxRuntimeExports.jsx(l.LockedInput,{value:t.name,disabled:!g,onSubmit:e=>y("name",e),label:"name"})},"name"),!i.includes("scope")&&n.jsxRuntimeExports.jsx(l.FormRow,{label:"Scope",children:n.jsxRuntimeExports.jsx(l.Dropdown,{options:M,disabled:t.builtin,value:t.scope||[],onChange:e=>y("scope",e),multiSelect:!0,widthExpand:!0})}),!i.includes("type")&&n.jsxRuntimeExports.jsx(l.FormRow,{label:"Type",children:n.jsxRuntimeExports.jsx(l.Dropdown,{value:[t?.data?.type],disabled:t.builtin||!g,options:Object.values(R),onChange:e=>{const a=e[0],o=R[a]?.fields?.includes("regex");m("type",a),!o&&t?.data?.regex&&m("regex","")},minSelected:1,widthExpand:!0})}),f.map(e=>{if(i.includes(e))return null;let a=null,s=S.upperFirst(e);if(e==="enum"||e==="inherit"){const o=w[e];a=o(t?.data[e],h=>m(e,h))}else if(e==="default"&&t?.data?.type==="boolean")a=w.booleanDefault(t?.data[e],o=>m(e,o));else if(["ge","gt","le","lt"].includes(e)){if(["gt","lt"].includes(e))return null;a=n.jsxRuntimeExports.jsx(T.MinMaxField,{value:t?.data,isMin:e==="ge",isFloat:t?.data?.type==="float",onChange:o=>{const h=o.ge!==void 0?Number(o.ge):void 0,E=o.le!==void 0?Number(o.le):void 0;o.ge!==void 0&&isNaN(h)||o.le!==void 0&&isNaN(E)||b(d=>{if(!d||!d.data)return d;const k={...d.data,...o};return{...d,data:k}})}}),s=e==="ge"?"Min":"Max"}else{const o=h=>{const E=h.target.value;switch(e){case"minLength":case"maxLength":case"minItems":case"maxItems":{const d=parseInt(E,10);m(e,isNaN(d)?void 0:d);break}default:m(e,E);break}};a=n.jsxRuntimeExports.jsx(l.InputText,{value:String(t?.data[e]??""),onChange:o})}return n.jsxRuntimeExports.jsx(l.FormRow,{label:s,style:{alignItems:"flex-start"},children:a},e)}),n.jsxRuntimeExports.jsx("span",{children:(v||x)&&n.jsxRuntimeExports.jsx("span",{className:"form-error-text",children:v||x})})]})})};exports.AttributeEditor=B;
|
|
2
2
|
//# sourceMappingURL=AttributeEditor.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttributeEditor.cjs.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n { value: 'list', label: 'List' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n {\n value: 'inherit',\n scope: ['project', 'folder', 'task', 'product', 'version', 'representation', 'user'],\n },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n datetime: {\n value: 'datetime',\n label: 'Datetime',\n fields: [],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: undefined,\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n if (typedKey !== 'data' && excludes.includes(typedKey)) return\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const initForm = buildInitFormData(excludes, { position: existingNames.length })\n const [formData, setFormData] = useState<AttributeForm | null>(attribute || initForm)\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,64}$')) error = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val?.length ? val : undefined)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n enableBackdropClose={false}\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin || !isNew}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => {\n const newType = v[0] as AttributeData['type']\n // Check if regex is supported for the new type\n const typeOpt = TYPE_OPTIONS[newType]\n const supportsRegex = typeOpt?.fields?.includes('regex')\n\n setData('type', newType)\n // Clear regex if not supported by the new type\n if (!supportsRegex && formData?.data?.regex) {\n setData('regex', '')\n }\n }}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["SCOPE_OPTIONS","GLOBAL_FIELDS","TYPE_OPTIONS","initFormData","buildInitFormData","excludes","data","formData","required","key","requiredData","typedKey","AttributeEditor","attribute","existingNames","error","isUpdating","onHide","onEdit","onDelete","initForm","setFormData","useState","useEffect","isNew","setTopLevelData","value","d","setData","dt","internalError","handleSubmit","footer","jsxs","jsx","Button","Spacer","SaveButton","dataFields","globalField","s","typeOpt","f","customFields","onChange","EnumEditor","val","InputSwitch","e","handleTitleChange","v","camelCase","Dialog","FormLayout","FormRow","InputText","LockedInput","Dropdown","newType","supportsRegex","field","fieldComp","fieldLabel","upperFirst","renderer","MinMaxField","geValue","leValue","handleInputChange","strValue","num"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AttributeEditor.cjs.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n { value: 'list', label: 'List' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n {\n value: 'inherit',\n scope: ['project', 'folder', 'task', 'product', 'version', 'representation', 'user'],\n },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n datetime: {\n value: 'datetime',\n label: 'Datetime',\n fields: [],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: undefined,\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n defaultData?: Partial<AttributeForm>\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n defaultData,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const resolvedDefaultData = defaultData\n ? {\n ...defaultData,\n name:\n defaultData.name ||\n (defaultData.data?.title ? camelCase(defaultData.data.title) : undefined),\n }\n : undefined\n\n const [formData, setFormData] = useState<AttributeForm | null>(\n attribute ||\n buildInitFormData(excludes, {\n position: existingNames.length,\n ...resolvedDefaultData,\n }),\n )\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,64}$')) error = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val?.length ? val : undefined)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n enableBackdropClose={false}\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin || !isNew}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => {\n const newType = v[0] as AttributeData['type']\n // Check if regex is supported for the new type\n const typeOpt = TYPE_OPTIONS[newType]\n const supportsRegex = typeOpt?.fields?.includes('regex')\n\n setData('type', newType)\n // Clear regex if not supported by the new type\n if (!supportsRegex && formData?.data?.regex) {\n setData('regex', '')\n }\n }}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["SCOPE_OPTIONS","GLOBAL_FIELDS","TYPE_OPTIONS","initFormData","buildInitFormData","excludes","data","formData","required","key","requiredData","typedKey","AttributeEditor","attribute","defaultData","existingNames","error","isUpdating","onHide","onEdit","onDelete","resolvedDefaultData","camelCase","setFormData","useState","useEffect","isNew","setTopLevelData","value","d","setData","dt","internalError","handleSubmit","footer","jsxs","jsx","Button","Spacer","SaveButton","dataFields","globalField","s","typeOpt","f","customFields","onChange","EnumEditor","val","InputSwitch","e","handleTitleChange","v","Dialog","FormLayout","FormRow","InputText","LockedInput","Dropdown","newType","supportsRegex","field","fieldComp","fieldLabel","upperFirst","renderer","MinMaxField","geValue","leValue","handleInputChange","strValue","num"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { j as n } from "../../../../_virtual/jsx-runtime.es.js";
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Dialog as
|
|
4
|
-
import {
|
|
5
|
-
import { MinMaxField as
|
|
6
|
-
import { EnumEditor as
|
|
2
|
+
import { useState as A, useEffect as B } from "react";
|
|
3
|
+
import { Dialog as M, FormLayout as K, FormRow as h, InputText as E, LockedInput as R, Dropdown as L, Button as V, Spacer as z, SaveButton as q, InputSwitch as N } from "@ynput/ayon-react-components";
|
|
4
|
+
import { camelCase as k, upperFirst as J } from "lodash";
|
|
5
|
+
import { MinMaxField as G } from "./components/MinMaxField.es.js";
|
|
6
|
+
import { EnumEditor as Y } from "../EnumEditor/EnumEditor.es.js";
|
|
7
7
|
import "../EnumEditor/EnumEditor.styled.es.js";
|
|
8
|
-
const
|
|
8
|
+
const Z = [
|
|
9
9
|
{ value: "project", label: "Project" },
|
|
10
10
|
{ value: "folder", label: "Folder" },
|
|
11
11
|
{ value: "task", label: "Task" },
|
|
@@ -14,7 +14,7 @@ const Y = [
|
|
|
14
14
|
{ value: "representation", label: "Representation" },
|
|
15
15
|
{ value: "user", label: "User" },
|
|
16
16
|
{ value: "list", label: "List" }
|
|
17
|
-
],
|
|
17
|
+
], $ = [
|
|
18
18
|
{ value: "description", scope: null },
|
|
19
19
|
{ value: "example", scope: null },
|
|
20
20
|
// @ts-expect-error - project is not a scope?
|
|
@@ -55,7 +55,7 @@ const Y = [
|
|
|
55
55
|
label: "Datetime",
|
|
56
56
|
fields: []
|
|
57
57
|
}
|
|
58
|
-
},
|
|
58
|
+
}, H = {
|
|
59
59
|
name: "",
|
|
60
60
|
scope: ["folder", "task"],
|
|
61
61
|
builtin: !1,
|
|
@@ -77,49 +77,58 @@ const Y = [
|
|
|
77
77
|
le: void 0,
|
|
78
78
|
lt: void 0
|
|
79
79
|
}
|
|
80
|
-
},
|
|
81
|
-
const
|
|
82
|
-
Object.keys(
|
|
83
|
-
!
|
|
80
|
+
}, Q = (c, r) => {
|
|
81
|
+
const o = JSON.parse(JSON.stringify(H)), p = ["name"];
|
|
82
|
+
Object.keys(o).forEach((i) => {
|
|
83
|
+
!p.includes(i) && c.includes(i) && i !== "data" && delete o[i];
|
|
84
84
|
});
|
|
85
|
-
const
|
|
86
|
-
return
|
|
87
|
-
!
|
|
88
|
-
}),
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
}),
|
|
92
|
-
},
|
|
93
|
-
attribute:
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
const b = ["title"];
|
|
86
|
+
return o.data && Object.keys(o.data).forEach((i) => {
|
|
87
|
+
!b.includes(i) && c.includes(i) && delete o.data[i];
|
|
88
|
+
}), r && Object.keys(r).forEach((i) => {
|
|
89
|
+
const m = i;
|
|
90
|
+
m === "data" && r.data && o.data ? o.data = { ...o.data, ...r.data } : r[m] !== void 0 && (o[m] = r[m]);
|
|
91
|
+
}), o;
|
|
92
|
+
}, le = ({
|
|
93
|
+
attribute: c,
|
|
94
|
+
defaultData: r,
|
|
95
|
+
existingNames: o,
|
|
96
|
+
error: p = "",
|
|
96
97
|
isUpdating: b,
|
|
97
|
-
excludes:
|
|
98
|
-
onHide:
|
|
99
|
-
onEdit:
|
|
98
|
+
excludes: i = [],
|
|
99
|
+
onHide: m,
|
|
100
|
+
onEdit: w,
|
|
100
101
|
onDelete: C
|
|
101
102
|
}) => {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
r
|
|
105
|
-
}, [
|
|
106
|
-
|
|
103
|
+
const F = r ? {
|
|
104
|
+
...r,
|
|
105
|
+
name: r.name || (r.data?.title ? k(r.data.title) : void 0)
|
|
106
|
+
} : void 0, [t, x] = A(
|
|
107
|
+
c || Q(i, {
|
|
108
|
+
position: o.length,
|
|
109
|
+
...F
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
B(() => {
|
|
113
|
+
c && x(c);
|
|
114
|
+
}, [c]);
|
|
115
|
+
const v = !c, S = (e, a) => {
|
|
107
116
|
x((l) => l && { ...l, [e]: a });
|
|
108
|
-
},
|
|
117
|
+
}, d = (e, a) => {
|
|
109
118
|
x((l) => {
|
|
110
119
|
if (!l || !l.data)
|
|
111
120
|
return l;
|
|
112
|
-
const
|
|
113
|
-
return { ...l, data:
|
|
121
|
+
const s = { ...l.data, [e]: a };
|
|
122
|
+
return { ...l, data: s };
|
|
114
123
|
});
|
|
115
124
|
};
|
|
116
|
-
let
|
|
117
|
-
t &&
|
|
118
|
-
const
|
|
119
|
-
t &&
|
|
120
|
-
},
|
|
121
|
-
C &&
|
|
122
|
-
|
|
125
|
+
let f = "";
|
|
126
|
+
t && v && (o.includes(t.name) ? f = "This attribute already exists" : t.name.match("^[a-zA-Z_]{2,64}$") || (p = "Invalid attribute name"));
|
|
127
|
+
const O = () => {
|
|
128
|
+
t && w(t);
|
|
129
|
+
}, T = /* @__PURE__ */ n.jsxs("div", { style: { display: "flex", width: "100%", flexDirection: "row" }, children: [
|
|
130
|
+
C && c && /* @__PURE__ */ n.jsx(
|
|
131
|
+
V,
|
|
123
132
|
{
|
|
124
133
|
variant: "danger",
|
|
125
134
|
label: "Delete attribute",
|
|
@@ -128,29 +137,29 @@ const Y = [
|
|
|
128
137
|
onClick: C
|
|
129
138
|
}
|
|
130
139
|
),
|
|
131
|
-
/* @__PURE__ */ n.jsx(
|
|
140
|
+
/* @__PURE__ */ n.jsx(z, {}),
|
|
132
141
|
/* @__PURE__ */ n.jsx(
|
|
133
|
-
|
|
142
|
+
q,
|
|
134
143
|
{
|
|
135
|
-
label:
|
|
144
|
+
label: v ? "Create Attribute" : "Save Attribute",
|
|
136
145
|
icon: "check",
|
|
137
|
-
disabled: !!
|
|
138
|
-
active: !
|
|
146
|
+
disabled: !!f || !t,
|
|
147
|
+
active: !f && !!t,
|
|
139
148
|
saving: b,
|
|
140
|
-
onClick:
|
|
149
|
+
onClick: O
|
|
141
150
|
}
|
|
142
151
|
)
|
|
143
152
|
] });
|
|
144
153
|
let j = [];
|
|
145
|
-
if (
|
|
154
|
+
if ($.forEach((e) => {
|
|
146
155
|
(!e?.scope || e?.scope?.some((a) => t?.scope?.includes(a))) && j.push(e.value);
|
|
147
156
|
}), t?.data.type && I[t.data.type]) {
|
|
148
157
|
const e = I[t.data.type];
|
|
149
158
|
j = [...j, ...e.fields].filter((a) => !e.exclude?.includes(a));
|
|
150
159
|
}
|
|
151
|
-
const
|
|
160
|
+
const D = {
|
|
152
161
|
enum: (e = [], a) => /* @__PURE__ */ n.jsx(
|
|
153
|
-
|
|
162
|
+
Y,
|
|
154
163
|
{
|
|
155
164
|
values: e,
|
|
156
165
|
onChange: (l) => {
|
|
@@ -172,38 +181,38 @@ const Y = [
|
|
|
172
181
|
onChange: (l) => a(l.target.checked)
|
|
173
182
|
}
|
|
174
183
|
)
|
|
175
|
-
},
|
|
184
|
+
}, _ = (e) => {
|
|
176
185
|
const a = e.target.value;
|
|
177
|
-
|
|
186
|
+
d("title", a), v && S("name", k(a));
|
|
178
187
|
};
|
|
179
188
|
return /* @__PURE__ */ n.jsx(
|
|
180
|
-
|
|
189
|
+
M,
|
|
181
190
|
{
|
|
182
191
|
header: t?.data?.title || t?.name || "New attribute",
|
|
183
|
-
footer:
|
|
184
|
-
onClose:
|
|
192
|
+
footer: T,
|
|
193
|
+
onClose: m,
|
|
185
194
|
isOpen: !0,
|
|
186
195
|
style: { width: 700, zIndex: 999 },
|
|
187
196
|
size: "full",
|
|
188
197
|
enableBackdropClose: !1,
|
|
189
198
|
onKeyDown: (e) => {
|
|
190
|
-
e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(),
|
|
199
|
+
e.key === "Enter" && (e.metaKey || e.ctrlKey) && (e.preventDefault(), O());
|
|
191
200
|
},
|
|
192
|
-
children: t && /* @__PURE__ */ n.jsxs(
|
|
193
|
-
!
|
|
194
|
-
!
|
|
195
|
-
|
|
201
|
+
children: t && /* @__PURE__ */ n.jsxs(K, { children: [
|
|
202
|
+
!i.includes("title") && /* @__PURE__ */ n.jsx(h, { label: "Title", children: /* @__PURE__ */ n.jsx(E, { value: t?.data.title, onChange: _, autoFocus: !0 }) }, "title"),
|
|
203
|
+
!i.includes("name") && /* @__PURE__ */ n.jsx(h, { label: "Name", children: /* @__PURE__ */ n.jsx(
|
|
204
|
+
R,
|
|
196
205
|
{
|
|
197
206
|
value: t.name,
|
|
198
|
-
disabled: !
|
|
207
|
+
disabled: !v,
|
|
199
208
|
onSubmit: (e) => S("name", e),
|
|
200
209
|
label: "name"
|
|
201
210
|
}
|
|
202
211
|
) }, "name"),
|
|
203
|
-
!
|
|
212
|
+
!i.includes("scope") && /* @__PURE__ */ n.jsx(h, { label: "Scope", children: /* @__PURE__ */ n.jsx(
|
|
204
213
|
L,
|
|
205
214
|
{
|
|
206
|
-
options:
|
|
215
|
+
options: Z,
|
|
207
216
|
disabled: t.builtin,
|
|
208
217
|
value: t.scope || [],
|
|
209
218
|
onChange: (e) => S("scope", e),
|
|
@@ -211,65 +220,65 @@ const Y = [
|
|
|
211
220
|
widthExpand: !0
|
|
212
221
|
}
|
|
213
222
|
) }),
|
|
214
|
-
!
|
|
223
|
+
!i.includes("type") && /* @__PURE__ */ n.jsx(h, { label: "Type", children: /* @__PURE__ */ n.jsx(
|
|
215
224
|
L,
|
|
216
225
|
{
|
|
217
226
|
value: [t?.data?.type],
|
|
218
|
-
disabled: t.builtin || !
|
|
227
|
+
disabled: t.builtin || !v,
|
|
219
228
|
options: Object.values(I),
|
|
220
229
|
onChange: (e) => {
|
|
221
|
-
const a = e[0],
|
|
222
|
-
|
|
230
|
+
const a = e[0], s = I[a]?.fields?.includes("regex");
|
|
231
|
+
d("type", a), !s && t?.data?.regex && d("regex", "");
|
|
223
232
|
},
|
|
224
233
|
minSelected: 1,
|
|
225
234
|
widthExpand: !0
|
|
226
235
|
}
|
|
227
236
|
) }),
|
|
228
237
|
j.map((e) => {
|
|
229
|
-
if (
|
|
230
|
-
let a = null, l =
|
|
238
|
+
if (i.includes(e)) return null;
|
|
239
|
+
let a = null, l = J(e);
|
|
231
240
|
if (e === "enum" || e === "inherit") {
|
|
232
|
-
const
|
|
233
|
-
a =
|
|
241
|
+
const s = D[e];
|
|
242
|
+
a = s(t?.data[e], (g) => d(e, g));
|
|
234
243
|
} else if (e === "default" && t?.data?.type === "boolean")
|
|
235
|
-
a =
|
|
244
|
+
a = D.booleanDefault(
|
|
236
245
|
t?.data[e],
|
|
237
|
-
(
|
|
246
|
+
(s) => d(e, s)
|
|
238
247
|
);
|
|
239
248
|
else if (["ge", "gt", "le", "lt"].includes(e)) {
|
|
240
249
|
if (["gt", "lt"].includes(e)) return null;
|
|
241
250
|
a = /* @__PURE__ */ n.jsx(
|
|
242
|
-
|
|
251
|
+
G,
|
|
243
252
|
{
|
|
244
253
|
value: t?.data,
|
|
245
254
|
isMin: e === "ge",
|
|
246
255
|
isFloat: t?.data?.type === "float",
|
|
247
|
-
onChange: (
|
|
248
|
-
const
|
|
256
|
+
onChange: (s) => {
|
|
257
|
+
const g = s.ge !== void 0 ? Number(s.ge) : void 0, y = s.le !== void 0 ? Number(s.le) : void 0;
|
|
249
258
|
// @ts-expect-error
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (!
|
|
253
|
-
const
|
|
254
|
-
return { ...
|
|
259
|
+
s.ge !== void 0 && isNaN(g) || // @ts-expect-error
|
|
260
|
+
s.le !== void 0 && isNaN(y) || x((u) => {
|
|
261
|
+
if (!u || !u.data) return u;
|
|
262
|
+
const P = { ...u.data, ...s };
|
|
263
|
+
return { ...u, data: P };
|
|
255
264
|
});
|
|
256
265
|
}
|
|
257
266
|
}
|
|
258
267
|
), l = e === "ge" ? "Min" : "Max";
|
|
259
268
|
} else {
|
|
260
|
-
const
|
|
261
|
-
const y =
|
|
269
|
+
const s = (g) => {
|
|
270
|
+
const y = g.target.value;
|
|
262
271
|
switch (e) {
|
|
263
272
|
case "minLength":
|
|
264
273
|
case "maxLength":
|
|
265
274
|
case "minItems":
|
|
266
275
|
case "maxItems": {
|
|
267
|
-
const
|
|
268
|
-
|
|
276
|
+
const u = parseInt(y, 10);
|
|
277
|
+
d(e, isNaN(u) ? void 0 : u);
|
|
269
278
|
break;
|
|
270
279
|
}
|
|
271
280
|
default:
|
|
272
|
-
|
|
281
|
+
d(e, y);
|
|
273
282
|
break;
|
|
274
283
|
}
|
|
275
284
|
};
|
|
@@ -277,12 +286,12 @@ const Y = [
|
|
|
277
286
|
E,
|
|
278
287
|
{
|
|
279
288
|
value: String(t?.data[e] ?? ""),
|
|
280
|
-
onChange:
|
|
289
|
+
onChange: s
|
|
281
290
|
}
|
|
282
291
|
);
|
|
283
292
|
}
|
|
284
293
|
return /* @__PURE__ */ n.jsx(
|
|
285
|
-
|
|
294
|
+
h,
|
|
286
295
|
{
|
|
287
296
|
label: l,
|
|
288
297
|
style: {
|
|
@@ -293,12 +302,12 @@ const Y = [
|
|
|
293
302
|
e
|
|
294
303
|
);
|
|
295
304
|
}),
|
|
296
|
-
/* @__PURE__ */ n.jsx("span", { children: (
|
|
305
|
+
/* @__PURE__ */ n.jsx("span", { children: (f || p) && /* @__PURE__ */ n.jsx("span", { className: "form-error-text", children: f || p }) })
|
|
297
306
|
] })
|
|
298
307
|
}
|
|
299
308
|
);
|
|
300
309
|
};
|
|
301
310
|
export {
|
|
302
|
-
|
|
311
|
+
le as AttributeEditor
|
|
303
312
|
};
|
|
304
313
|
//# sourceMappingURL=AttributeEditor.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttributeEditor.es.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n { value: 'list', label: 'List' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n {\n value: 'inherit',\n scope: ['project', 'folder', 'task', 'product', 'version', 'representation', 'user'],\n },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n datetime: {\n value: 'datetime',\n label: 'Datetime',\n fields: [],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: undefined,\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n if (typedKey !== 'data' && excludes.includes(typedKey)) return\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const initForm = buildInitFormData(excludes, { position: existingNames.length })\n const [formData, setFormData] = useState<AttributeForm | null>(attribute || initForm)\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,64}$')) error = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val?.length ? val : undefined)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n enableBackdropClose={false}\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin || !isNew}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => {\n const newType = v[0] as AttributeData['type']\n // Check if regex is supported for the new type\n const typeOpt = TYPE_OPTIONS[newType]\n const supportsRegex = typeOpt?.fields?.includes('regex')\n\n setData('type', newType)\n // Clear regex if not supported by the new type\n if (!supportsRegex && formData?.data?.regex) {\n setData('regex', '')\n }\n }}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["SCOPE_OPTIONS","GLOBAL_FIELDS","TYPE_OPTIONS","initFormData","buildInitFormData","excludes","data","formData","required","key","requiredData","typedKey","AttributeEditor","attribute","existingNames","error","isUpdating","onHide","onEdit","onDelete","initForm","setFormData","useState","useEffect","isNew","setTopLevelData","value","d","setData","dt","internalError","handleSubmit","footer","jsxs","jsx","Button","Spacer","SaveButton","dataFields","globalField","s","typeOpt","f","customFields","onChange","EnumEditor","val","InputSwitch","e","handleTitleChange","v","camelCase","Dialog","FormLayout","FormRow","InputText","LockedInput","Dropdown","newType","supportsRegex","field","fieldComp","fieldLabel","upperFirst","renderer","MinMaxField","geValue","leValue","handleInputChange","strValue","num"],"mappings":";;;;;;;AAkBA,MAAMA,IAAgB;AAAA,EACpB,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,UAAU,OAAO,SAAA;AAAA,EAC1B,EAAE,OAAO,QAAQ,OAAO,OAAA;AAAA,EACxB,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,kBAAkB,OAAO,iBAAA;AAAA,EAClC,EAAE,OAAO,QAAQ,OAAO,OAAA;AAAA,EACxB,EAAE,OAAO,QAAQ,OAAO,OAAA;AAC1B,GAQMC,IAAoC;AAAA,EACxC,EAAE,OAAO,eAAe,OAAO,KAAA;AAAA,EAC/B,EAAE,OAAO,WAAW,OAAO,KAAA;AAAA;AAAA,EAE3B,EAAE,OAAO,WAAW,OAAO,CAAC,SAAS,EAAA;AAAA,EACrC;AAAA,IACE,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,UAAU,QAAQ,WAAW,WAAW,kBAAkB,MAAM;AAAA,EAAA;AAEvF,GAaMC,IAA+B;AAAA,EACnC,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAC,aAAa,aAAa,QAAQ,OAAO;AAAA,EAAA;AAAA,EAEpD,SAAS;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAC,MAAM,MAAM,MAAM,IAAI;AAAA,EAAA;AAAA,EAEjC,OAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAC,MAAM,MAAM,MAAM,IAAI;AAAA,EAAA;AAAA,EAEjC,iBAAiB;AAAA,IACf,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAC,YAAY,YAAY,MAAM;AAAA,EAAA;AAAA,EAEzC,SAAS;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAA;AAAA,IACR,SAAS,CAAC,SAAS;AAAA,EAAA;AAAA,EAErB,UAAU;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAA;AAAA,EAAC;AAEb,GAOMC,IAA8B;AAAA,EAClC,MAAM;AAAA,EACN,OAAO,CAAC,UAAU,MAAM;AAAA,EACxB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,WAAW;AAAA,IACX,WAAW;AAAA,IACX,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAER,GAGMC,IAAoB,CAACC,GAAoBC,MAAkC;AAE/E,QAAMC,IAAW,KAAK,MAAM,KAAK,UAAUJ,CAAY,CAAC,GAGlDK,IAAW,CAAC,MAAM;AACxB,SAAO,KAAKD,CAAQ,EAAE,QAAQ,CAACE,MAAQ;AACrC,IACE,CAACD,EAAS,SAASC,CAAG,KACtBJ,EAAS,SAASI,CAAyC,KAC3DA,MAAQ,UAER,OAAOF,EAASE,CAA0B;AAAA,EAE9C,CAAC;AAGD,QAAMC,IAAe,CAAC,OAAO;AAC7B,SAAIH,EAAS,QACX,OAAO,KAAKA,EAAS,IAAI,EAAE,QAAQ,CAACE,MAAQ;AAC1C,IAAI,CAACC,EAAa,SAASD,CAAG,KAAKJ,EAAS,SAASI,CAA0B,KAC7E,OAAOF,EAAS,KAAKE,CAA0B;AAAA,EAEnD,CAAC,GAICH,KAEF,OAAO,KAAKA,CAAI,EAAE,QAAQ,CAACG,MAAQ;AACjC,UAAME,IAAWF;AACjB,IAAIE,MAAa,UAAUN,EAAS,SAASM,CAAQ,MAEjDA,MAAa,UAAUL,EAAK,QAAQC,EAAS,OAE/CA,EAAS,OAAO,EAAE,GAAGA,EAAS,MAAM,GAAGD,EAAK,KAAA,IACnCA,EAAKK,CAAQ,MAAM,WAE5BJ,EAASI,CAAQ,IAAIL,EAAKK,CAAQ;AAAA,EAEtC,CAAC,GAGIJ;AACT,GAaaK,KAA4C,CAAC;AAAA,EACxD,WAAAC;AAAA,EACA,eAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,YAAAC;AAAA,EACA,UAAAX,IAAW,CAAA;AAAA,EACX,QAAAY;AAAA,EACA,QAAAC;AAAA,EACA,UAAAC;AACF,MAAM;AACJ,QAAMC,IAAWhB,EAAkBC,GAAU,EAAE,UAAUS,EAAc,QAAQ,GACzE,CAACP,GAAUc,CAAW,IAAIC,EAA+BT,KAAaO,CAAQ;AAEpF,EAAAG,EAAU,MAAM;AACd,IAAMV,KAAWQ,EAAYR,CAAS;AAAA,EACxC,GAAG,CAACA,CAAS,CAAC;AAEd,QAAMW,IAAQ,CAACX,GAGTY,IAAkB,CACtBhB,GACAiB,MACG;AACH,IAAAL,EAAY,CAACM,MACNA,KAGE,EAAE,GAAGA,GAAG,CAAClB,CAAG,GAAGiB,EAAA,CACvB;AAAA,EACH,GAGME,IAAU,CAAgCnB,GAAQiB,MAA4B;AAClF,IAAAL,EAAY,CAACM,MAAM;AAEjB,UAAI,CAACA,KAAK,CAACA,EAAE;AACX,eAAOA;AAET,YAAME,IAAK,EAAE,GAAGF,EAAE,MAAM,CAAClB,CAAG,GAAGiB,EAAA;AAC/B,aAAO,EAAE,GAAGC,GAAG,MAAME,EAAA;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,MAAIC,IAAgB;AACpB,EAAIvB,KACEiB,MACEV,EAAc,SAASP,EAAS,IAAI,IAAGuB,IAAgB,kCACjDvB,EAAS,KAAK,MAAM,mBAAmB,MAAGQ,IAAQ;AAIhE,QAAMgB,IAAe,MAAM;AACzB,IAAIxB,KACFW,EAAOX,CAAQ;AAAA,EAEnB,GAEMyB,IACJC,gBAAAA,EAAAA,KAAC,OAAA,EAAI,OAAO,EAAE,SAAS,QAAQ,OAAO,QAAQ,eAAe,MAAA,GAC1D,UAAA;AAAA,IAAAd,KAAYN,KACXqB,gBAAAA,EAAAA;AAAAA,MAACC;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,OAAO;AAAA,QACP,MAAM;AAAA,QACN,UAAUnB;AAAA,QACV,SAASG;AAAA,MAAA;AAAA,IAAA;AAAA,0BAGZiB,GAAA,EAAO;AAAA,IACRF,gBAAAA,EAAAA;AAAAA,MAACG;AAAA,MAAA;AAAA,QACC,OAAOb,IAAQ,qBAAqB;AAAA,QACpC,MAAM;AAAA,QACN,UAAU,CAAC,CAACM,KAAiB,CAACvB;AAAA,QAC9B,QAAQ,CAACuB,KAAiB,CAAC,CAACvB;AAAA,QAC5B,QAAQS;AAAA,QACR,SAASe;AAAA,MAAA;AAAA,IAAA;AAAA,EACX,GACF;AAGF,MAAIO,IAAsC,CAAA;AAU1C,MAPArC,EAAc,QAAQ,CAACsC,MAAgB;AAErC,KAAI,CAACA,GAAa,SAASA,GAAa,OAAO,KAAK,CAACC,MAAMjC,GAAU,OAAO,SAASiC,CAAC,CAAC,MACrFF,EAAW,KAAKC,EAAY,KAAK;AAAA,EAErC,CAAC,GAEGhC,GAAU,KAAK,QAAQL,EAAaK,EAAS,KAAK,IAAI,GAAG;AAC3D,UAAMkC,IAAUvC,EAAaK,EAAS,KAAK,IAAI;AAC/C,IAAA+B,IAAa,CAAC,GAAGA,GAAY,GAAGG,EAAQ,MAAM,EAAE,OAAO,CAACC,MAAM,CAACD,EAAQ,SAAS,SAASC,CAAC,CAAC;AAAA,EAC7F;AAGA,QAAMC,IAIF;AAAA,IACF,MAAM,CAACjB,IAAQ,CAAA,GAAIkB,MACjBV,gBAAAA,EAAAA;AAAAA,MAACW;AAAA,MAAA;AAAA,QACC,QAAQnB;AAAA,QACR,UAAU,CAACoB,MAAQ;AACjB,UAAAF,EAASE,GAAK,SAASA,IAAM,MAAS;AAAA,QACxC;AAAA,MAAA;AAAA,IAAA;AAAA,IAGJ,SAAS,CAACpB,GAAOkB,MACfV,gBAAAA,EAAAA;AAAAA,MAACa;AAAA,MAAA;AAAA,QACC,SAASrB;AAAA,QACT,UAAU,CAACsB,MAAMJ,EAAUI,EAAE,OAA4B,OAAO;AAAA,MAAA;AAAA,IAAA;AAAA,IAGpE,gBAAgB,CAACtB,GAAOkB,MACtBV,gBAAAA,EAAAA;AAAAA,MAACa;AAAA,MAAA;AAAA,QACC,SAASrB;AAAA,QACT,UAAU,CAACsB,MAAMJ,EAAUI,EAAE,OAA4B,OAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAClE,GAIEC,IAAoB,CAAC,MAAyB;AAClD,UAAMC,IAAK,EAAE,OAA4B;AACzC,IAAAtB,EAAQ,SAASsB,CAAC,GAEd1B,KACFC,EAAgB,QAAQ0B,EAAUD,CAAC,CAAC;AAAA,EAExC;AAEA,SACEhB,gBAAAA,EAAAA;AAAAA,IAACkB;AAAA,IAAA;AAAA,MACC,QAAQ7C,GAAU,MAAM,SAASA,GAAU,QAAQ;AAAA,MACnD,QAAAyB;AAAA,MACA,SAASf;AAAA,MACT,QAAQ;AAAA,MACR,OAAO,EAAE,OAAO,KAAK,QAAQ,IAAA;AAAA,MAC7B,MAAK;AAAA,MACL,qBAAqB;AAAA,MACrB,WAAW,CAAC,MAA2C;AACrD,QAAI,EAAE,QAAQ,YAAY,EAAE,WAAW,EAAE,aACvC,EAAE,eAAA,GACFc,EAAA;AAAA,MAEJ;AAAA,MAEC,UAAAxB,4BACE8C,GAAA,EACE,UAAA;AAAA,QAAA,CAAChD,EAAS,SAAS,OAAO,2BACxBiD,GAAA,EAAQ,OAAO,SACd,UAAApB,gBAAAA,MAACqB,GAAA,EAAU,OAAOhD,GAAU,KAAK,OAAU,UAAU0C,GAAmB,WAAS,IAAC,KADtD,OAE9B;AAAA,QAED,CAAC5C,EAAS,SAAS,MAAM,KACxB6B,gBAAAA,EAAAA,IAACoB,GAAA,EAAQ,OAAO,QACd,UAAApB,gBAAAA,EAAAA;AAAAA,UAACsB;AAAA,UAAA;AAAA,YACC,OAAOjD,EAAS;AAAA,YAChB,UAAU,CAACiB;AAAA,YACX,UAAU,CAAC0B,MAAMzB,EAAgB,QAAQyB,CAAC;AAAA,YAC1C,OAAM;AAAA,UAAA;AAAA,QAAA,KALmB,MAO7B;AAAA,QAED,CAAC7C,EAAS,SAAS,OAAO,KACzB6B,gBAAAA,EAAAA,IAACoB,GAAA,EAAQ,OAAM,SACb,UAAApB,gBAAAA,EAAAA;AAAAA,UAACuB;AAAA,UAAA;AAAA,YACC,SAASzD;AAAA,YACT,UAAUO,EAAS;AAAA,YACnB,OAAOA,EAAS,SAAS,CAAA;AAAA,YACzB,UAAU,CAAC2C,MAAMzB,EAAgB,SAASyB,CAA4B;AAAA,YACtE,aAAW;AAAA,YACX,aAAW;AAAA,UAAA;AAAA,QAAA,GAEf;AAAA,QAED,CAAC7C,EAAS,SAAS,MAAM,KACxB6B,gBAAAA,EAAAA,IAACoB,GAAA,EAAQ,OAAM,QACb,UAAApB,gBAAAA,EAAAA;AAAAA,UAACuB;AAAA,UAAA;AAAA,YACC,OAAO,CAAClD,GAAU,MAAM,IAAI;AAAA,YAC5B,UAAUA,EAAS,WAAW,CAACiB;AAAA,YAC/B,SAAS,OAAO,OAAOtB,CAAY;AAAA,YACnC,UAAU,CAACgD,MAAM;AACf,oBAAMQ,IAAUR,EAAE,CAAC,GAGbS,IADUzD,EAAawD,CAAO,GACL,QAAQ,SAAS,OAAO;AAEvD,cAAA9B,EAAQ,QAAQ8B,CAAO,GAEnB,CAACC,KAAiBpD,GAAU,MAAM,SACpCqB,EAAQ,SAAS,EAAE;AAAA,YAEvB;AAAA,YACA,aAAa;AAAA,YACb,aAAW;AAAA,UAAA;AAAA,QAAA,GAEf;AAAA,QAEDU,EAAW,IAAI,CAACsB,MAAU;AAEzB,cAAIvD,EAAS,SAASuD,CAAK,EAAG,QAAO;AAErC,cAAIC,IAAY,MACZC,IAAaC,EAAWH,CAAK;AAEjC,cAAIA,MAAU,UAAUA,MAAU,WAAW;AAC3C,kBAAMI,IAAWrB,EAAaiB,CAA2B;AACzD,YAAAC,IAAYG,EAASzD,GAAU,KAAKqD,CAAK,GAAG,CAAClC,MAAUE,EAAQgC,GAAOlC,CAAK,CAAC;AAAA,UAC9E,WAAWkC,MAAU,aAAarD,GAAU,MAAM,SAAS;AACzD,YAAAsD,IAAYlB,EAAa;AAAA,cACvBpC,GAAU,KAAKqD,CAAK;AAAA,cACpB,CAAClC,MAAUE,EAAQgC,GAAOlC,CAAiC;AAAA,YAAA;AAAA,mBAEpD,CAAC,MAAM,MAAM,MAAM,IAAI,EAAE,SAASkC,CAAK,GAAG;AAEnD,gBAAI,CAAC,MAAM,IAAI,EAAE,SAASA,CAAK,EAAG,QAAO;AACzC,YAAAC,IACE3B,gBAAAA,EAAAA;AAAAA,cAAC+B;AAAA,cAAA;AAAA,gBACC,OAAO1D,GAAU;AAAA,gBACjB,OAAOqD,MAAU;AAAA,gBACjB,SAASrD,GAAU,MAAM,SAAS;AAAA,gBAClC,UAAU,CAAC2C,MAAM;AACf,wBAAMgB,IAAUhB,EAAE,OAAO,SAAY,OAAOA,EAAE,EAAE,IAAI,QAC9CiB,IAAUjB,EAAE,OAAO,SAAY,OAAOA,EAAE,EAAE,IAAI;AAEpD;AAAA,kBAEGA,EAAE,OAAO,UAAa,MAAMgB,CAAO;AAAA,kBAEnChB,EAAE,OAAO,UAAa,MAAMiB,CAAO,KAMtC9C,EAAY,CAACM,MAAM;AACjB,wBAAI,CAACA,KAAK,CAACA,EAAE,KAAM,QAAOA;AAC1B,0BAAME,IAAK,EAAE,GAAGF,EAAE,MAAM,GAAGuB,EAAA;AAC3B,2BAAO,EAAE,GAAGvB,GAAG,MAAME,EAAA;AAAA,kBACvB,CAAC;AAAA,gBACH;AAAA,cAAA;AAAA,YAAA,GAKJiC,IAAaF,MAAU,OAAO,QAAQ;AAAA,UACxC,OAAO;AACL,kBAAMQ,IAAoB,CAACpB,MAA2C;AACpE,oBAAMqB,IAAWrB,EAAE,OAAO;AAC1B,sBAAQY,GAAA;AAAA,gBACN,KAAK;AAAA,gBACL,KAAK;AAAA,gBACL,KAAK;AAAA,gBACL,KAAK,YAAY;AACf,wBAAMU,IAAM,SAASD,GAAU,EAAE;AACjC,kBAAAzC,EAAQgC,GAAO,MAAMU,CAAG,IAAI,SAAYA,CAAG;AAC3C;AAAA,gBACF;AAAA,gBACA;AAEE,kBAAA1C,EAAQgC,GAAOS,CAAuC;AACtD;AAAA,cAAA;AAAA,YAEN;AAEA,YAAAR,IACE3B,gBAAAA,EAAAA;AAAAA,cAACqB;AAAA,cAAA;AAAA,gBACC,OAAO,OAAOhD,GAAU,KAAKqD,CAAK,KAAK,EAAE;AAAA,gBACzC,UAAUQ;AAAA,cAAA;AAAA,YAAA;AAAA,UAGhB;AAEA,iBACElC,gBAAAA,EAAAA;AAAAA,YAACoB;AAAA,YAAA;AAAA,cACC,OAAOQ;AAAA,cAEP,OAAO;AAAA,gBACL,YAAY;AAAA,cAAA;AAAA,cAGb,UAAAD;AAAA,YAAA;AAAA,YALID;AAAA,UAAA;AAAA,QAQX,CAAC;AAAA,QACD1B,gBAAAA,EAAAA,IAAC,QAAA,EACG,WAAAJ,KAAiBf,MACjBmB,gBAAAA,EAAAA,IAAC,UAAK,WAAU,mBAAmB,UAAAJ,KAAiBf,EAAA,CAAM,EAAA,CAE9D;AAAA,MAAA,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
|
1
|
+
{"version":3,"file":"AttributeEditor.es.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n { value: 'list', label: 'List' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n {\n value: 'inherit',\n scope: ['project', 'folder', 'task', 'product', 'version', 'representation', 'user'],\n },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n datetime: {\n value: 'datetime',\n label: 'Datetime',\n fields: [],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: undefined,\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n defaultData?: Partial<AttributeForm>\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n defaultData,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const resolvedDefaultData = defaultData\n ? {\n ...defaultData,\n name:\n defaultData.name ||\n (defaultData.data?.title ? camelCase(defaultData.data.title) : undefined),\n }\n : undefined\n\n const [formData, setFormData] = useState<AttributeForm | null>(\n attribute ||\n buildInitFormData(excludes, {\n position: existingNames.length,\n ...resolvedDefaultData,\n }),\n )\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,64}$')) error = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val?.length ? val : undefined)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n enableBackdropClose={false}\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin || !isNew}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => {\n const newType = v[0] as AttributeData['type']\n // Check if regex is supported for the new type\n const typeOpt = TYPE_OPTIONS[newType]\n const supportsRegex = typeOpt?.fields?.includes('regex')\n\n setData('type', newType)\n // Clear regex if not supported by the new type\n if (!supportsRegex && formData?.data?.regex) {\n setData('regex', '')\n }\n }}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["SCOPE_OPTIONS","GLOBAL_FIELDS","TYPE_OPTIONS","initFormData","buildInitFormData","excludes","data","formData","required","key","requiredData","typedKey","AttributeEditor","attribute","defaultData","existingNames","error","isUpdating","onHide","onEdit","onDelete","resolvedDefaultData","camelCase","setFormData","useState","useEffect","isNew","setTopLevelData","value","d","setData","dt","internalError","handleSubmit","footer","jsxs","jsx","Button","Spacer","SaveButton","dataFields","globalField","s","typeOpt","f","customFields","onChange","EnumEditor","val","InputSwitch","e","handleTitleChange","v","Dialog","FormLayout","FormRow","InputText","LockedInput","Dropdown","newType","supportsRegex","field","fieldComp","fieldLabel","upperFirst","renderer","MinMaxField","geValue","leValue","handleInputChange","strValue","num"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("../../../../_virtual/jsx-runtime.cjs.js"),g=require("@ynput/ayon-react-components"),i=require("react"),l=require("styled-components"),j=require("clsx"),I=l.keyframes`
|
|
2
2
|
from {
|
|
3
3
|
transform: rotate(0deg);
|
|
4
4
|
}
|
|
5
5
|
to {
|
|
6
6
|
transform: rotate(360deg);
|
|
7
7
|
}
|
|
8
|
-
`,
|
|
8
|
+
`,T=l.div`
|
|
9
9
|
position: relative;
|
|
10
10
|
display: flex;
|
|
11
11
|
justify-content: center;
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
overflow: hidden;
|
|
15
15
|
|
|
16
16
|
background-color: var(--md-sys-color-surface-container-low);
|
|
17
|
-
`,
|
|
18
|
-
animation: ${
|
|
17
|
+
`,L=l(g.Icon)`
|
|
18
|
+
animation: ${I} 1s linear infinite;
|
|
19
19
|
opacity: 0.6;
|
|
20
|
-
`,
|
|
20
|
+
`,q=l.img`
|
|
21
21
|
position: absolute;
|
|
22
22
|
inset: 0;
|
|
23
23
|
object-fit: cover;
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
&.hidden {
|
|
30
30
|
opacity: 0;
|
|
31
31
|
}
|
|
32
|
-
`,
|
|
32
|
+
`,F=({mimetype:E="",src:p,...u})=>{const[R,r]=i.useState(!1),[w,t]=i.useState(!1),s=i.useRef(null),a=i.useRef(0);return i.useEffect(()=>{const n=a.current+1;a.current=n,t(!1),r(!1);const e=s.current;let h=!1,d=0,m=0,f=0;const o=b=>{h||n!==a.current||(h=!0,cancelAnimationFrame(d),window.clearInterval(m),window.clearTimeout(f),b())},v=()=>{!e||n!==a.current||e.complete&&e.naturalWidth>0&&o(()=>{t(!0),r(!1)})},x=()=>o(()=>{t(!0),r(!1)}),y=()=>o(()=>{t(!0),r(!0)});return e&&(e.addEventListener("load",x),e.addEventListener("error",y),typeof e.decode=="function"&&e.decode().then(()=>o(()=>{t(!0),r(!1)})).catch(()=>{v()})),d=requestAnimationFrame(v),m=window.setInterval(v,250),f=window.setTimeout(()=>{o(()=>{e&&e.naturalWidth>0?(t(!0),r(!1)):t(!0)})},1e4),()=>{cancelAnimationFrame(d),window.clearInterval(m),window.clearTimeout(f),e&&(e.removeEventListener("load",x),e.removeEventListener("error",y))}},[p]),c.jsxRuntimeExports.jsxs(T,{...u,className:j("file-thumbnail",u.className),children:[w?c.jsxRuntimeExports.jsx(g.Icon,{icon:g.getMimeTypeIcon(E)}):c.jsxRuntimeExports.jsx(L,{icon:"progress_activity"}),c.jsxRuntimeExports.jsx(q,{ref:s,src:p,...u,onError:n=>{n.currentTarget===s.current&&(t(!0),r(!0))},onLoad:n=>{n.currentTarget===s.current&&(t(!0),r(!1))},className:j({hidden:!w||R})})]})};exports.FileThumbnail=F;
|
|
33
33
|
//# sourceMappingURL=FileThumbnail.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileThumbnail.cjs.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n\n useEffect(() => {\n // reset
|
|
1
|
+
{"version":3,"file":"FileThumbnail.cjs.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState, useRef } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const loadAttemptRef = useRef(0)\n\n // Robust loading effect: attempt to detect when the image is actually ready.\n // Uses `decode()` when available, falls back to `complete` + `naturalWidth` checks,\n // adds RAF + polling and a watchdog timeout to avoid stuck states.\n useEffect(() => {\n const attemptId = loadAttemptRef.current + 1\n loadAttemptRef.current = attemptId\n\n // reset visible state for this attempt\n setLoaded(false)\n setError(false)\n\n const img = imageRef.current\n let resolved = false\n let frame = 0\n let interval = 0\n let timeout = 0\n\n const resolveOnce = (resolver: () => void) => {\n if (resolved || attemptId !== loadAttemptRef.current) return\n resolved = true\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n resolver()\n }\n\n const checkComplete = () => {\n if (!img || attemptId !== loadAttemptRef.current) return\n if (img.complete && img.naturalWidth > 0) {\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n }\n }\n\n const onLoad = () =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n const onError = () =>\n resolveOnce(() => {\n // keep behavior: mark loaded so placeholder swaps to icon, and flag error\n setLoaded(true)\n setError(true)\n })\n\n if (img) {\n img.addEventListener('load', onLoad)\n img.addEventListener('error', onError)\n\n if (typeof img.decode === 'function') {\n img\n .decode()\n .then(() =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n }),\n )\n .catch(() => {\n checkComplete()\n })\n }\n }\n\n frame = requestAnimationFrame(checkComplete)\n interval = window.setInterval(checkComplete, 250)\n\n timeout = window.setTimeout(() => {\n // Force-resolve after a timeout to avoid infinite spinner states.\n resolveOnce(() => {\n if (img && img.naturalWidth > 0) {\n setLoaded(true)\n setError(false)\n } else {\n setLoaded(true)\n }\n })\n }, 10000)\n\n return () => {\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n if (img) {\n img.removeEventListener('load', onLoad)\n img.removeEventListener('error', onError)\n }\n }\n }, [src])\n\n return (\n <Wrapper {...props} className={clsx('file-thumbnail', props.className)}>\n {!loaded ? (\n <LoadingIcon icon=\"progress_activity\" />\n ) : (\n <Icon icon={getMimeTypeIcon(mimetype)} />\n )}\n <Image\n ref={imageRef}\n src={src}\n {...props}\n onError={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(true)\n }\n }}\n onLoad={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(false)\n }\n }}\n className={clsx({ hidden: !loaded || error })}\n />\n </Wrapper>\n )\n}\n"],"names":["spin","keyframes","Wrapper","styled","LoadingIcon","Icon","Image","FileThumbnail","mimetype","src","props","error","setError","useState","loaded","setLoaded","imageRef","useRef","loadAttemptRef","useEffect","attemptId","img","resolved","frame","interval","timeout","resolveOnce","resolver","checkComplete","onLoad","onError","jsxs","clsx","jsx","getMimeTypeIcon","event"],"mappings":"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"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { j as
|
|
2
|
-
import { Icon as
|
|
3
|
-
import { useState as
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
const
|
|
1
|
+
import { j as s } from "../../../../_virtual/jsx-runtime.es.js";
|
|
2
|
+
import { Icon as j, getMimeTypeIcon as b } from "@ynput/ayon-react-components";
|
|
3
|
+
import { useState as y, useRef as x, useEffect as k } from "react";
|
|
4
|
+
import u, { keyframes as A } from "styled-components";
|
|
5
|
+
import E from "clsx";
|
|
6
|
+
const F = A`
|
|
7
7
|
from {
|
|
8
8
|
transform: rotate(0deg);
|
|
9
9
|
}
|
|
10
10
|
to {
|
|
11
11
|
transform: rotate(360deg);
|
|
12
12
|
}
|
|
13
|
-
`,
|
|
13
|
+
`, R = u.div`
|
|
14
14
|
position: relative;
|
|
15
15
|
display: flex;
|
|
16
16
|
justify-content: center;
|
|
@@ -19,10 +19,10 @@ const g = p`
|
|
|
19
19
|
overflow: hidden;
|
|
20
20
|
|
|
21
21
|
background-color: var(--md-sys-color-surface-container-low);
|
|
22
|
-
`,
|
|
23
|
-
animation: ${
|
|
22
|
+
`, N = u(j)`
|
|
23
|
+
animation: ${F} 1s linear infinite;
|
|
24
24
|
opacity: 0.6;
|
|
25
|
-
`,
|
|
25
|
+
`, W = u.img`
|
|
26
26
|
position: absolute;
|
|
27
27
|
inset: 0;
|
|
28
28
|
object-fit: cover;
|
|
@@ -34,29 +34,57 @@ const g = p`
|
|
|
34
34
|
&.hidden {
|
|
35
35
|
opacity: 0;
|
|
36
36
|
}
|
|
37
|
-
`,
|
|
38
|
-
const [
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
`, _ = ({ mimetype: I = "", src: v, ...c }) => {
|
|
38
|
+
const [L, r] = y(!1), [p, t] = y(!1), a = x(null), i = x(0);
|
|
39
|
+
return k(() => {
|
|
40
|
+
const o = i.current + 1;
|
|
41
|
+
i.current = o, t(!1), r(!1);
|
|
42
|
+
const e = a.current;
|
|
43
|
+
let w = !1, l = 0, m = 0, d = 0;
|
|
44
|
+
const n = (T) => {
|
|
45
|
+
w || o !== i.current || (w = !0, cancelAnimationFrame(l), window.clearInterval(m), window.clearTimeout(d), T());
|
|
46
|
+
}, f = () => {
|
|
47
|
+
!e || o !== i.current || e.complete && e.naturalWidth > 0 && n(() => {
|
|
48
|
+
t(!0), r(!1);
|
|
49
|
+
});
|
|
50
|
+
}, g = () => n(() => {
|
|
51
|
+
t(!0), r(!1);
|
|
52
|
+
}), h = () => n(() => {
|
|
53
|
+
t(!0), r(!0);
|
|
54
|
+
});
|
|
55
|
+
return e && (e.addEventListener("load", g), e.addEventListener("error", h), typeof e.decode == "function" && e.decode().then(
|
|
56
|
+
() => n(() => {
|
|
57
|
+
t(!0), r(!1);
|
|
58
|
+
})
|
|
59
|
+
).catch(() => {
|
|
60
|
+
f();
|
|
61
|
+
})), l = requestAnimationFrame(f), m = window.setInterval(f, 250), d = window.setTimeout(() => {
|
|
62
|
+
n(() => {
|
|
63
|
+
e && e.naturalWidth > 0 ? (t(!0), r(!1)) : t(!0);
|
|
64
|
+
});
|
|
65
|
+
}, 1e4), () => {
|
|
66
|
+
cancelAnimationFrame(l), window.clearInterval(m), window.clearTimeout(d), e && (e.removeEventListener("load", g), e.removeEventListener("error", h));
|
|
67
|
+
};
|
|
68
|
+
}, [v]), /* @__PURE__ */ s.jsxs(R, { ...c, className: E("file-thumbnail", c.className), children: [
|
|
69
|
+
p ? /* @__PURE__ */ s.jsx(j, { icon: b(I) }) : /* @__PURE__ */ s.jsx(N, { icon: "progress_activity" }),
|
|
70
|
+
/* @__PURE__ */ s.jsx(
|
|
71
|
+
W,
|
|
45
72
|
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
ref: a,
|
|
74
|
+
src: v,
|
|
75
|
+
...c,
|
|
76
|
+
onError: (o) => {
|
|
77
|
+
o.currentTarget === a.current && (t(!0), r(!0));
|
|
50
78
|
},
|
|
51
|
-
onLoad: () => {
|
|
52
|
-
|
|
79
|
+
onLoad: (o) => {
|
|
80
|
+
o.currentTarget === a.current && (t(!0), r(!1));
|
|
53
81
|
},
|
|
54
|
-
className:
|
|
82
|
+
className: E({ hidden: !p || L })
|
|
55
83
|
}
|
|
56
84
|
)
|
|
57
85
|
] });
|
|
58
86
|
};
|
|
59
87
|
export {
|
|
60
|
-
|
|
88
|
+
_ as FileThumbnail
|
|
61
89
|
};
|
|
62
90
|
//# sourceMappingURL=FileThumbnail.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileThumbnail.es.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n\n useEffect(() => {\n // reset
|
|
1
|
+
{"version":3,"file":"FileThumbnail.es.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState, useRef } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const loadAttemptRef = useRef(0)\n\n // Robust loading effect: attempt to detect when the image is actually ready.\n // Uses `decode()` when available, falls back to `complete` + `naturalWidth` checks,\n // adds RAF + polling and a watchdog timeout to avoid stuck states.\n useEffect(() => {\n const attemptId = loadAttemptRef.current + 1\n loadAttemptRef.current = attemptId\n\n // reset visible state for this attempt\n setLoaded(false)\n setError(false)\n\n const img = imageRef.current\n let resolved = false\n let frame = 0\n let interval = 0\n let timeout = 0\n\n const resolveOnce = (resolver: () => void) => {\n if (resolved || attemptId !== loadAttemptRef.current) return\n resolved = true\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n resolver()\n }\n\n const checkComplete = () => {\n if (!img || attemptId !== loadAttemptRef.current) return\n if (img.complete && img.naturalWidth > 0) {\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n }\n }\n\n const onLoad = () =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n const onError = () =>\n resolveOnce(() => {\n // keep behavior: mark loaded so placeholder swaps to icon, and flag error\n setLoaded(true)\n setError(true)\n })\n\n if (img) {\n img.addEventListener('load', onLoad)\n img.addEventListener('error', onError)\n\n if (typeof img.decode === 'function') {\n img\n .decode()\n .then(() =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n }),\n )\n .catch(() => {\n checkComplete()\n })\n }\n }\n\n frame = requestAnimationFrame(checkComplete)\n interval = window.setInterval(checkComplete, 250)\n\n timeout = window.setTimeout(() => {\n // Force-resolve after a timeout to avoid infinite spinner states.\n resolveOnce(() => {\n if (img && img.naturalWidth > 0) {\n setLoaded(true)\n setError(false)\n } else {\n setLoaded(true)\n }\n })\n }, 10000)\n\n return () => {\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n if (img) {\n img.removeEventListener('load', onLoad)\n img.removeEventListener('error', onError)\n }\n }\n }, [src])\n\n return (\n <Wrapper {...props} className={clsx('file-thumbnail', props.className)}>\n {!loaded ? (\n <LoadingIcon icon=\"progress_activity\" />\n ) : (\n <Icon icon={getMimeTypeIcon(mimetype)} />\n )}\n <Image\n ref={imageRef}\n src={src}\n {...props}\n onError={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(true)\n }\n }}\n onLoad={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(false)\n }\n }}\n className={clsx({ hidden: !loaded || error })}\n />\n </Wrapper>\n )\n}\n"],"names":["spin","keyframes","Wrapper","styled","LoadingIcon","Icon","Image","FileThumbnail","mimetype","src","props","error","setError","useState","loaded","setLoaded","imageRef","useRef","loadAttemptRef","useEffect","attemptId","img","resolved","frame","interval","timeout","resolveOnce","resolver","checkComplete","onLoad","onError","jsxs","clsx","jsx","getMimeTypeIcon","event"],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ export type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>;
|
|
|
6
6
|
type Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[];
|
|
7
7
|
export interface AttributeEditorProps {
|
|
8
8
|
attribute: AttributeForm | null;
|
|
9
|
+
defaultData?: Partial<AttributeForm>;
|
|
9
10
|
existingNames: string[];
|
|
10
11
|
error?: string;
|
|
11
12
|
isUpdating?: boolean;
|