New combobox implementation

This commit is contained in:
Elio Struyf
2023-11-01 12:55:28 +01:00
parent 6cbe76096b
commit 3a8dcbe22f
8 changed files with 166 additions and 8 deletions
+25 -5
View File
@@ -12,7 +12,7 @@
"@actions/core": "^1.10.0",
"@bendera/vscode-webview-elements": "0.6.2",
"@estruyf/vscode": "^1.1.0",
"@headlessui/react": "1.5.0",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "1.0.4",
"@iarna/toml": "2.2.3",
"@octokit/rest": "^18.12.0",
@@ -348,9 +348,13 @@
}
},
"node_modules/@headlessui/react": {
"version": "1.5.0",
"version": "1.7.17",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz",
"integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==",
"dev": true,
"license": "MIT",
"dependencies": {
"client-only": "^0.0.1"
},
"engines": {
"node": ">=10"
},
@@ -2960,6 +2964,12 @@
"node": ">= 4.0"
}
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"dev": true
},
"node_modules/clipboardy": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
@@ -13314,9 +13324,13 @@
}
},
"@headlessui/react": {
"version": "1.5.0",
"version": "1.7.17",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz",
"integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==",
"dev": true,
"requires": {}
"requires": {
"client-only": "^0.0.1"
}
},
"@heroicons/react": {
"version": "1.0.4",
@@ -15272,6 +15286,12 @@
"source-map": "~0.6.0"
}
},
"client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"dev": true
},
"clipboardy": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
+1 -1
View File
@@ -2526,7 +2526,7 @@
"@actions/core": "^1.10.0",
"@bendera/vscode-webview-elements": "0.6.2",
"@estruyf/vscode": "^1.1.0",
"@headlessui/react": "1.5.0",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "1.0.4",
"@iarna/toml": "2.2.3",
"@octokit/rest": "^18.12.0",
@@ -0,0 +1,100 @@
import { Combobox, Transition } from '@headlessui/react';
import * as React from 'react';
import { BaseFieldProps } from '../../../models';
import { Fragment, useEffect, useMemo } from 'react';
import { Page } from '../../../dashboardWebView/models';
import { messageHandler } from '@estruyf/vscode/dist/client/webview';
import { CommandToCode } from '../../CommandToCode';
import { ChevronDownIcon } from '@heroicons/react/outline';
export interface IComboboxFieldProps extends BaseFieldProps<string | string[]> {
contentTypeName?: string;
contentTypeValue?: string;
multiSelect?: boolean;
onChange: (value: string | string[]) => void;
}
export const ComboboxField: React.FunctionComponent<IComboboxFieldProps> = ({
label,
description,
value,
contentTypeName,
contentTypeValue,
multiSelect,
onChange,
required
}: React.PropsWithChildren<IComboboxFieldProps>) => {
const [loading, setLoading] = React.useState<boolean>(false);
const [choices, setChoices] = React.useState<string[]>([]);
const [pages, setPages] = React.useState<Page[]>([]);
const [crntSelected, setCrntSelected] = React.useState<string | string[] | null>(value);
const availableChoices = useMemo(() => {
return pages.filter((page: Page) => {
const value = contentTypeValue === "slug" ? page.slug : page.fmFilePath;
if (typeof crntSelected === 'string') {
return crntSelected !== `${value}` && !value.includes(crntSelected);
} else if (crntSelected instanceof Array) {
const selected = crntSelected.filter((v) => v === `${value}` || value.includes(v));
return selected.length === 0;
}
return true;
});
}, [choices, crntSelected, multiSelect, contentTypeValue]);
useEffect(() => {
if (contentTypeName) {
setLoading(true);
messageHandler
.request<Page[]>(CommandToCode.searchByType, contentTypeName)
.then((pages: Page[]) => {
setPages(pages || []);
setChoices((pages || []).map(page => page.title))
}).finally(() => {
setLoading(false);
});
}
}, [contentTypeName]);
return (
<Combobox
value={crntSelected}
onChange={(value) => null}
>
<div className="relative mt-1">
<div className="relative w-full cursor-default overflow-hidden text-left focus:outline-none">
<Combobox.Input
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
onChange={(e) => console.log(e)} />
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2 w-8">
<ChevronDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Combobox.Options
className="absolute mt-1 max-h-60 w-full overflow-auto py-1 text-base focus:outline-none z-50 bg-black">
{availableChoices.map((choice) => (
<Combobox.Option key={choice.fmFilePath} value={choice.fmFileName}>
{choice.title}
</Combobox.Option>
))}
</Combobox.Options>
</Transition>
</div>
</Combobox>
)
};
@@ -33,6 +33,7 @@ import { fieldWhenClause } from '../../../utils/fieldWhenClause';
import { ContentTypeRelationshipField } from './ContentTypeRelationshipField';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { ComboboxField } from './ComboboxField';
export interface IWrapperFieldProps {
field: Field;
@@ -485,7 +486,18 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
} else if (field.type === 'contentRelationship') {
return (
<FieldBoundary key={field.name} fieldName={field.title || field.name}>
<ContentTypeRelationshipField
{/* <ContentTypeRelationshipField
label={field.title || field.name}
description={field.description}
value={fieldValue as string}
required={!!field.required}
contentTypeName={field.contentTypeName}
contentTypeValue={field.contentTypeValue}
multiSelect={field.multiple}
onChange={(value) => onSendUpdate(field.name, value, parentFields)}
/> */}
<ComboboxField
label={field.title || field.name}
description={field.description}
value={fieldValue as string}
+1
View File
@@ -5,6 +5,7 @@ import * as Sentry from '@sentry/react';
import { Integrations } from '@sentry/tracing';
import { SENTRY_LINK, SentryIgnore } from '../constants';
import { RecoilRoot } from 'recoil';
import './styles.css';
// require('@vscode/codicons/dist/codicon.css');
+3
View File
@@ -1,3 +1,6 @@
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
/* Animations */
@keyframes spin {
from {
+8
View File
@@ -0,0 +1,8 @@
const defaultConfig = require("./tailwind.config")
module.exports = {
...defaultConfig,
corePlugins: {
preflight: false,
}
}
+15 -1
View File
@@ -31,7 +31,21 @@ const config = [{
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
use: ['style-loader', 'css-loader', {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {
config: path.resolve(__dirname, '../tailwind.panel.js')
},
autoprefixer: {},
},
},
},
}]
},
{
test: /\.m?js/,