diff --git a/src/containers/SettingsEditor/FormTemplates/FieldTemplate.tsx b/src/containers/SettingsEditor/FormTemplates/FieldTemplate.tsx index c2b9e1b10..7da23bddf 100644 --- a/src/containers/SettingsEditor/FormTemplates/FieldTemplate.tsx +++ b/src/containers/SettingsEditor/FormTemplates/FieldTemplate.tsx @@ -10,6 +10,7 @@ import { FieldTemplateProps } from '@rjsf/utils' import { CSS } from 'styled-components/dist/types' import { matchesFilterKeys } from './searchMatcher' import { toast } from 'react-toastify' +import AccessWidget from '../Widgets/AccessWidget' const arrayStartsWith = (arr1: $Any, arr2: $Any) => { // return true, if first array starts with second array @@ -54,8 +55,9 @@ function FieldTemplate(props: FieldTemplateProps) { }, [section, filterKeys]) // Object fields + - if (props.schema.type === 'object') { + if (props.schema.type === 'object' && props.schema?.widget !== 'access') { return ( <> {divider} @@ -204,6 +206,19 @@ function FieldTemplate(props: FieldTemplateProps) { props.id, ) + + let mainWidget = null + if (props.schema.widget === 'access') { + mainWidget = ( + + ) + } else { + mainWidget = props.children + } + + + + return (
)} -
{props.children}
+ +
{mainWidget}
) diff --git a/src/containers/SettingsEditor/FormTemplates/ObjectFieldTemplate.tsx b/src/containers/SettingsEditor/FormTemplates/ObjectFieldTemplate.tsx index 05b596843..3d0bdc999 100644 --- a/src/containers/SettingsEditor/FormTemplates/ObjectFieldTemplate.tsx +++ b/src/containers/SettingsEditor/FormTemplates/ObjectFieldTemplate.tsx @@ -21,6 +21,7 @@ const arrayStartsWith = (arr1: $Any, arr2: $Any) => { return true } + function ObjectFieldTemplate(props: { id: string } & ObjectFieldTemplateProps) { const [contextMenu] = useCreateContextMenu([]) let className = 'form-object-field' @@ -78,6 +79,7 @@ function ObjectFieldTemplate(props: { id: string } & ObjectFieldTemplateProps) { }, [props.properties]) const fields = useMemo(() => { + let hiddenFields: $Any[] = [] for (const propName in props?.schema?.properties || {}) { //@ts-ignore @@ -96,6 +98,7 @@ function ObjectFieldTemplate(props: { id: string } & ObjectFieldTemplateProps) { } } + if (props.schema.layout === 'expanded') { let nameField = null let otherFields = [] @@ -162,6 +165,7 @@ function ObjectFieldTemplate(props: { id: string } & ObjectFieldTemplateProps) { // aaand... render + if (['compact', 'root', 'expanded'].includes(props.schema.layout)) return fields const contextMenuModel = useMemo(() => { diff --git a/src/containers/SettingsEditor/SettingsEditor.sass b/src/containers/SettingsEditor/SettingsEditor.sass index 4f597d6b8..e66468779 100644 --- a/src/containers/SettingsEditor/SettingsEditor.sass +++ b/src/containers/SettingsEditor/SettingsEditor.sass @@ -125,6 +125,7 @@ $field-gap: 8px .form-inline-field display: flex + min-width: 250px flex-grow: 1 flex-direction: row align-items: center @@ -135,7 +136,6 @@ $field-gap: 8px .form-inline-field-label flex-basis: 250px - min-width: 250px padding-left: 4px cursor: pointer user-select: none diff --git a/src/containers/SettingsEditor/SettingsEditor.styled.tsx b/src/containers/SettingsEditor/SettingsEditor.styled.tsx index 6d425760f..e944d93fd 100644 --- a/src/containers/SettingsEditor/SettingsEditor.styled.tsx +++ b/src/containers/SettingsEditor/SettingsEditor.styled.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components' -const FormWrapper = styled.div<{$currentSelection: string}>` +const FormWrapper = styled.div<{ $currentSelection: string }>` [data-fieldid='${(props) => props.$currentSelection}'] { border-radius: 4px; background-color: rgba(0, 0, 0, 0.2); @@ -39,4 +39,45 @@ const FormWrapper = styled.div<{$currentSelection: string}>` } } ` -export { FormWrapper } + + +const CodeEditorWrapper = styled.div` + position: relative; + display: flex; + resize: vertical; + overflow: auto; + flex-direction: column; + min-height: 40px; + height: 200px; + border: 1px solid var(--md-sys-color-outline-variant); + border-radius: var(--border-radius-m); + + .wrap { + position: relative; + top: 0; + left: 0; + overflow: scroll; + } + .w-tc-editor { + background-color: var(--md-sys-color-surface-container-low); + flex-grow: 1; + overflow: unset !important; + * { + background-color: var(--md-sys-color-surface-container-low); + font-family: monospace !important; + font-size: 12px; + } + } + + &.error { + border: 1px solid var(--md-sys-color-error); + } + + &.changed { + border: 1px solid var(--md-sys-color-primary); + } + +` + + +export { FormWrapper, CodeEditorWrapper } diff --git a/src/containers/SettingsEditor/Widgets/AccessWidget.jsx b/src/containers/SettingsEditor/Widgets/AccessWidget.jsx new file mode 100644 index 000000000..f2ac666a8 --- /dev/null +++ b/src/containers/SettingsEditor/Widgets/AccessWidget.jsx @@ -0,0 +1,202 @@ +import axios from 'axios' +import styled from 'styled-components' + +import { toast } from 'react-toastify' +import { useState, useEffect, useMemo } from 'react' +import { updateChangedKeys, parseContext, getDefaultValue } from '../helpers' +import CodeEditor from '@uiw/react-textarea-code-editor' +import { CodeEditorWrapper } from '../SettingsEditor.styled' +import { Button, Dialog } from '@ynput/ayon-react-components' + + +const ShareOption = styled.div` + background: #434a56; + border-radius: 4px; + padding: 8px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +` + +const ShareOptionsContainer = styled.div` + display: flex; + flex-direction: column; + gap: 8px; + overflow-y: auto; +` + + + + +const AccessEditorDialog = ({ initialValue, onSubmit, projectName }) => { + + const [shareOptions, setShareOptions] = useState([]) + const [values, setValues] = useState(initialValue || {}) + + useEffect(() => { + const fetchShareOptions = async () => { + try { + const response = await axios.get('/api/share', {params: { project_name: projectName }}) + const result = [ + { label: 'Internal users', value: '__everyone__', 'shareType': 'global' }, + { label: 'Guest users', value: '__guests__', 'shareType': 'global' }, + ] + for (const option of response.data.options) { + result.push({ label: option.label, value: option.value, shareType: option.shareType }) + } + + setShareOptions(result) + } catch (error) { + console.error('Error fetching share options:', error) + toast.error('Failed to fetch share options') + } + } + + fetchShareOptions() + }, [projectName]) + + + const footer = ( +
+
+ ) + + return ( + onSubmit(null)} + style={{ width: '600px', height: '600px' }} + > + + {shareOptions.map((option) => ( + + {option.label} ({option.shareType}) + 0} + onChange={(e) => { + const newValue = { ...values, [option.value]: e.target.checked ? 10 : undefined } + console.log(newValue) + setValues(newValue) + }} + /> + + ))} + + + ) +} + + + + +const AccessWidget = (props) => { + const { path } = parseContext(props) + const projectName = props?.formContext?.headerProjectName + + const [value, setValue] = useState(props.formData || {}) + const [isOpen, setIsOpen] = useState(false) + + const onDialogSubmit = (commitValue) => { + console.log("Dialog submitted with value:", commitValue) + if (commitValue === null) { + setIsOpen(false) + return + } + + const isChanged = commitValue !== value + setValue(commitValue) + props.onChange(commitValue) + setTimeout(() => { + updateChangedKeys(props, isChanged, path) + setStatus('unchanged') + }, 100) + setIsOpen(false) + } + + + const dialogComponent = useMemo(() => { + if (!isOpen) return + return ( + + ) + }, [value, isOpen, projectName]) + + + return ( + <> +