Skip to content

Commit

Permalink
init package
Browse files Browse the repository at this point in the history
  • Loading branch information
PRGfx committed Oct 4, 2023
0 parents commit e391ea4
Show file tree
Hide file tree
Showing 17 changed files with 3,305 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
15 changes: 15 additions & 0 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Neos:
Neos:
Ui:
resources:
javascript:
'Prgx.Neos.StringsEditor':
resource: 'resource://Prgfx.Neos.StringsEditor/Public/Plugin/Plugin.js'
stylesheets:
'Prgx.Neos.StringsEditor':
resource: 'resource://Prgfx.Neos.StringsEditor/Public/Plugin/Plugin.css'
userInterface:
inspector:
dataTypes:
array<string>:
editor: Prgfx.Neos.StringsEditor
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Prgfx.Neos.StringsEditor

Provides an inspector editor for editing simple `array<string>` properties.

`composer require prgfx/neos-stringseditor`

![Screenshot of the editor](./screenshot.png)

## Usage
```yaml
My.NodeType:
properties:
items:
type: array<string>
ui:
inspector:
# you can normally omit this, when you use array<string> as property type
editor: Prgfx.Neos.StringsEditor
editorOptions:
placeholder: Placeholder text
# optional number of items allowed to be entered
maximumItems: 4
# by default only unique items are allowed, but you can disable this rule
unique: false
```
91 changes: 91 additions & 0 deletions Resources/Private/Plugin/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
module.exports = {
'env': {
'browser': true,
'es2021': true,
},
'extends': [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
],
'overrides': [
{
'env': {
'node': true,
},
'files': [
'.eslintrc.{js,cjs}',
],
'parserOptions': {
'sourceType': 'script',
},
},
],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 'latest',
'sourceType': 'module',
},
'plugins': [
'@typescript-eslint',
'react',
'react-hooks',
],
'rules': {
'indent': [
'error',
4,
],
'quotes': [
'error',
'single',
],
'semi': [
'error',
'always',
],
'comma-dangle': [
'error',
{
'arrays': 'always-multiline',
'objects': 'always-multiline',
'imports': 'always-multiline',
'exports': 'always-multiline',
'functions': 'ignore',
},
],
'object-curly-spacing': [
'warn',
'always',
{
'objectsInObjects': true,
'arraysInObjects': true,
},
],
'array-bracket-spacing': [
'warn',
'always',
],
'jsx-quotes': [
'error',
'prefer-double',
],
'react/jsx-curly-spacing': [
'error',
{
'when': 'never',
'children': true,
},
],
'react/jsx-curly-brace-presence': [
'error',
{
'props': 'never',
'children': 'ignore',
'propElementValues': 'always',
},
],
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
},
};
28 changes: 28 additions & 0 deletions Resources/Private/Plugin/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-env node */
import esbuild from "esbuild";
import eslint from "esbuild-plugin-eslint";
import {cssModules} from "esbuild-plugin-lightningcss-modules";
import extensibilityMap from "@neos-project/neos-ui-extensibility/extensibilityMap.json" assert { type: 'json' };

const isWatch = process.argv.includes('--watch');

/** @type {import("esbuild").BuildOptions} */
const options = {
logLevel: 'info',
bundle: true,
target: 'es2020',
entryPoints: { 'Plugin': 'src/index.js' },
outdir: '../../Public/Plugin',
alias: extensibilityMap,
plugins: [
eslint(),
cssModules({}),
],
};

if (isWatch) {
esbuild.context(options).then((ctx) => ctx.watch());
} else {
options.minify = true;
esbuild.build(options);
}
28 changes: 28 additions & 0 deletions Resources/Private/Plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@prgfx/neos-stringseditor",
"private": true,
"scripts": {
"build": "node build.mjs",
"watch": "node build.mjs --watch",
"lint": "eslint src/"
},
"devDependencies": {
"@neos-project/neos-ui-extensibility": "~8.3.0",
"@neos-project/react-ui-components": "~8.3.0",
"@types/react": "^17.0.6",
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2",
"esbuild": "~0.17.18",
"esbuild-plugin-eslint": "^0.3.6",
"esbuild-plugin-lightningcss-modules": "^0.1.0",
"eslint": "^8.50.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"typescript": "^5.2.2"
},
"peerDependencies": {
"classnames": "^2.3.1",
"react": "^17.0.2",
"react-redux": "^7.2.4"
}
}
21 changes: 21 additions & 0 deletions Resources/Private/Plugin/src/StringsEditor.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.inputWrapper {
display: flex;
}

.inputWrapper > div {
flex-grow: 1;
}

.list {
margin-bottom: 0;
}

.input {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

.submitButton {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
128 changes: 128 additions & 0 deletions Resources/Private/Plugin/src/StringsEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useMemo, useState } from 'react';
import {
IconButton,
MultiSelectBox_ListPreviewSortable,
SelectBox_Option_SingleLine,
TextInput,
} from '@neos-project/react-ui-components';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import styles from './StringsEditor.module.css';

type StringsEditorProps = {
id: string;
value: string[];
options: unknown;
commit: (value: string[]) => void;
i18nRegistry: { translate: (key: string) => string };
disabled?: boolean;
}

const valueAccessor = (x: { key: string }): string => x.key;

const isObject = (e: unknown): e is Record<string, unknown> => {
return typeof e === 'object' && e !== null && !Array.isArray(e);
};

type StringsEditorOptions = {
unique: boolean;
placeholder?: string;
maximumItems?: number;
}

const getOptions = (options: unknown): StringsEditorOptions => {
const result: StringsEditorOptions = {
unique: true,
};

if (isObject(options)) {
if ('unique' in options) {
result.unique = options.unique !== false;
}

if (typeof options.placeholder === 'string') {
result.placeholder = options.placeholder;
}

if (typeof options.maximumItems === 'number' && result.maximumItems > 0) {
result.maximumItems = options.maximumItems;
}
}

return result;
};

export const StringsEditor = (props: StringsEditorProps) => {
console.log('StringsEditor', props);
const [ input, setInput ] = useState('');

const {
unique,
placeholder,
maximumItems,
} = useMemo(() => {
return getOptions(props.options);
}, [ props.options ]);

const inputEmpty = input.trim().length === 0;
const inputUnique = !unique || !props.value.includes(input);
const hasMaxItems = maximumItems !== undefined && props.value.length >= maximumItems;
const inputValid = !inputEmpty && inputUnique && !hasMaxItems;

const handleSubmit = () => {
if (!inputValid) {
return;
}

props.commit([ ...props.value, input ]);
setInput('');
};

const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
handleSubmit();
}
};

const options = useMemo(() => {
return props.value.map(item => ({
label: item,
key: item,
}));
}, [ props.value ]);

return (
<div>
<ul className={styles.list}>
<MultiSelectBox_ListPreviewSortable
disabled={props.disabled}
values={props.value}
options={options}
optionValueAccessor={valueAccessor}
onValuesChange={props.commit}
ListPreviewElement={SelectBox_Option_SingleLine}
dndType="multiselect-box-value"
/>
</ul>
<div className={styles.inputWrapper}>
<TextInput
id={props.id}
value={input}
onChange={setInput}
onKeyUp={handleKeyPress}
disabled={props.disabled || hasMaxItems}
placeholder={placeholder ? props.i18nRegistry.translate(placeholder) : undefined}
className={styles.input}
/>
<IconButton
icon="check"
onClick={handleSubmit}
disabled={props.disabled || !inputValid}
className={styles.submitButton}
style="neutral"
/>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions Resources/Private/Plugin/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './manifest';
9 changes: 9 additions & 0 deletions Resources/Private/Plugin/src/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import manifest from '@neos-project/neos-ui-extensibility';
import { StringsEditor } from './StringsEditor';

manifest('Prgfx.Neos.StringsEditor:StringsEditor', {}, (globalRegistry) => {
const editorRegistry = globalRegistry.get('inspector').get('editors');
editorRegistry.set('Prgfx.Neos.StringsEditor', {
component: StringsEditor,
});
});
11 changes: 11 additions & 0 deletions Resources/Private/Plugin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"lib": [
"DOM",
"ES2022"
],
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "react"
}
}
Loading

0 comments on commit e391ea4

Please sign in to comment.