Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ljmotta committed Oct 29, 2024
1 parent 5ebd85c commit 53363e3
Show file tree
Hide file tree
Showing 13 changed files with 498 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { createAutoField } from "uniforms/cjs/createAutoField";

import TextField from "./TextField";
import BoolField from "./BoolField";
import ListField from "./ListField";
import NumField from "./NumField";
import NestField from "./NestField";
import DateField from "./DateField";
Expand All @@ -40,10 +41,8 @@ const AutoField = createAutoField((props) => {
}

switch (props.fieldType) {
/*
TODO: implement array support
case Array:
return ListField;*/
return ListField;
case Boolean:
return BoolField;
case Date:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { useContext } from "react";
import { connectField, context, HTMLFieldProps, joinName } from "uniforms/cjs";
import { getInputReference, getStateCode, renderField } from "./utils/Utils";
import { codeGenContext } from "./CodeGenContext";
import { FormInput, InputReference } from "../api";
import { ARRAY } from "./utils/dataTypes";
import { renderListItemFragmentWithContext } from "./rendering/RenderingUtils";
import { ListItemProps } from "./rendering/ListItemField";

export type ListFieldProps = HTMLFieldProps<
unknown[],
HTMLDivElement,
{
itemProps?: ListItemProps;
maxCount?: number;
minCount?: number;
}
>;

const List: React.FC<ListFieldProps> = (props: ListFieldProps) => {
const ref: InputReference = getInputReference(props.name, ARRAY);

const uniformsContext = useContext(context);
const codegenCtx = useContext(codeGenContext);

const listItem = renderListItemFragmentWithContext(
uniformsContext,
"$",
{
isListItem: true,
indexVariableName: "itemIndex",
listName: props.name,
listStateName: ref.stateName,
listStateSetter: ref.stateSetter,
},
props.disabled
);
const jsxCode = `<div fieldId={'${props.id}'}>
<Split hasGutter>
<SplitItem>
{'${props.label}' && (
<label>
'${props.label}'
</label>
)}
</SplitItem>
<SplitItem isFilled />
<SplitItem>
<Button
name='$'
variant='plain'
style={{ paddingLeft: '0', paddingRight: '0' }}
disabled={${props.maxCount === undefined ? props.disabled : `${props.disabled} || !(${props.maxCount} <= (${ref.stateName}?.length ?? -1))`}}
onClick={() => {
!${props.disabled} && ${props.maxCount === undefined ? `${ref.stateSetter}((${ref.stateName} ?? []).concat([]))` : `!(${props.maxCount} <= (${ref.stateName}?.length ?? -1)) && ${ref.stateSetter}((${ref.stateName} ?? []).concat([]))`};
}}
>
+
{/* <PlusCircleIcon color='#0088ce' /> */}
</Button>
</SplitItem>
</Split>
<div>
{${ref.stateName}?.map((_, itemIndex) =>
(<div
key={itemIndex}
style={{
marginBottom: '1rem',
display: 'flex',
justifyContent: 'space-between',
}}
>
<div style={{ width: '100%', marginRight: '10px' }}>${listItem?.jsxCode}</div>
<div>
<Button
disabled={${props.minCount === undefined ? props.disabled : `${props.disabled} || (${props.minCount} >= (${ref.stateName}?.length ?? -1))`}}
variant='plain'
style={{ paddingLeft: '0', paddingRight: '0' }}
onClick={() => {
const value = ${ref.stateName}!.slice();
value.splice(${+joinName(null, "")[joinName(null, "").length - 1]}, 1);
!${props.disabled} && ${props.minCount === undefined ? `${ref.stateSetter}(value)` : `!(${props.minCount} >= (${ref.stateName}?.length ?? -1)) && ${ref.stateSetter}(value)`};
}}
>
- { /* <MinusCircleIcon color='#cc0000' /> */}
</Button>
</div>
</div>)
)}
</div>
</div>`;

// TODO ADD PLUS AND MINUS ICONS
const element: FormInput = {
ref,
pfImports: [...new Set(["Split", "SplitItem", "Button", ...(listItem?.pfImports ?? [])])],
reactImports: [...new Set([...(listItem?.reactImports ?? [])])],
requiredCode: [...new Set([...(listItem?.requiredCode ?? [])])],
jsxCode,
stateCode: getStateCode(ref.stateName, ref.stateSetter, "any[]", "[]"),
isReadonly: props.disabled,
};

codegenCtx?.rendered.push(element);

return renderField(element);
};

export default connectField(List);
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { codeGenContext } from "./CodeGenContext";
import { union } from "lodash";
import { OBJECT } from "./utils/dataTypes";

export type NestFieldProps = HTMLFieldProps<object, HTMLDivElement, { itemProps?: object }>;
export type NestFieldProps = HTMLFieldProps<object, HTMLDivElement, { itemProps?: any }>;

const Nest: React.FunctionComponent<NestFieldProps> = ({
id,
Expand Down Expand Up @@ -75,7 +75,7 @@ const Nest: React.FunctionComponent<NestFieldProps> = ({
});
}

const bodyLabel = label ? `<label><b>${label}</b></label>` : "";
const bodyLabel = label && !itemProps?.isListItem ? `<label><b>${label}</b></label>` : "";

const stateCode = nestedStates.join("\n");
const jsxCode = `<Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { buildDefaultInputElement, getInputReference, renderField } from "./util
import { useAddFormElementToContext } from "./CodeGenContext";
import { FormInput, InputReference } from "../api";
import { NUMBER } from "./utils/dataTypes";
import { getListItemName, getListItemOnChange, getListItemValue, ListItemProps } from "./rendering/ListItemField";

export type NumFieldProps = HTMLFieldProps<
string,
Expand All @@ -34,6 +35,7 @@ export type NumFieldProps = HTMLFieldProps<
decimal?: boolean;
min?: string;
max?: string;
itemProps?: ListItemProps;
}
>;

Expand All @@ -45,13 +47,13 @@ const Num: React.FC<NumFieldProps> = (props: NumFieldProps) => {

const inputJsxCode = `<TextInput
type={'number'}
name={'${props.name}'}
name={${props.itemProps?.isListItem ? getListItemName(props.itemProps, props.name) : `'${props.name}'`}}
isDisabled={${props.disabled || "false"}}
id={'${props.id}'}
placeholder={'${props.placeholder}'}
step={${props.decimal ? 0.01 : 1}} ${max} ${min}
value={${ref.stateName}}
onChange={(newValue) => ${ref.stateSetter}(Number(newValue))}
value={${props.itemProps?.isListItem ? getListItemValue(props.itemProps, props.name) : ref.stateName}}
onChange={${props.itemProps?.isListItem ? getListItemOnChange(props.itemProps, props.name, (value: string) => `Number(${value})`) : `(newValue) => ${ref.stateSetter}(Number(newValue))`}}
/>`;

const element: FormInput = buildDefaultInputElement({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ import { FormInput, InputReference } from "../api";
import { buildDefaultInputElement, getInputReference, renderField } from "./utils/Utils";
import { DATE_FUNCTIONS } from "./staticCode/staticCodeBlocks";
import { DATE, STRING } from "./utils/dataTypes";
import { getListItemName, getListItemOnChange, getListItemValue, ListItemProps } from "./rendering/ListItemField";

export type TextFieldProps = HTMLFieldProps<
string,
HTMLInputElement,
{
label: string;
required: boolean;
itemProps?: ListItemProps;
}
>;

Expand All @@ -44,9 +46,9 @@ const Text: React.FC<TextFieldProps> = (props: TextFieldProps) => {
const inputJsxCode = `<DatePicker
id={'date-picker-${props.id}'}
isDisabled={${props.disabled || false}}
name={'${props.name}'}
onChange={newDate => onDateChange(newDate, ${ref.stateSetter}, ${ref.stateName})}
value={parseDate(${ref.stateName})}
name={${props.itemProps?.isListItem ? getListItemName(props.itemProps, props.name) : `'${props.name}'`}}
onChange={${props.itemProps?.isListItem ? getListItemOnChange(props.itemProps, props.name, (value: string) => `onDateChange(${value}, ${ref.stateSetter}, ${ref.stateName})`) : `newDate => onDateChange(newDate, ${ref.stateSetter}, ${ref.stateName})`}}
value={${props.itemProps?.isListItem ? `parseDate(${getListItemValue(props.itemProps, props.name)})` : `parseDate(${ref.stateName})`}}
/>`;
return buildDefaultInputElement({
pfImports: ["DatePicker"],
Expand All @@ -64,13 +66,13 @@ const Text: React.FC<TextFieldProps> = (props: TextFieldProps) => {

const getTextInputElement = (): FormInput => {
const inputJsxCode = `<TextInput
name={'${props.name}'}
name={${props.itemProps?.isListItem ? getListItemName(props.itemProps, props.name) : `'${props.name}'`}}
id={'${props.id}'}
isDisabled={${props.disabled || "false"}}
placeholder={'${props.placeholder}'}
type={'${props.type || "text"}'}
value={${ref.stateName}}
onChange={${ref.stateSetter}}
value={${props.itemProps?.isListItem ? getListItemValue(props.itemProps, props.name) : ref.stateName}}
onChange={${props.itemProps?.isListItem ? getListItemOnChange(props.itemProps, props.name) : ref.stateSetter}}
/>`;

return buildDefaultInputElement({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as AutoFields } from "./AutoFields";
export { default as BoolField } from "./BoolField";
export { default as CheckBoxGroupField } from "./CheckBoxGroupField";
export { default as DateField } from "./DateField";
export { default as ListField } from "./ListField";
export { default as NestField } from "./NestField";
export { default as NumField } from "./NumField";
export { default as RadioField } from "./RadioField";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { Context } from "uniforms";
import * as React from "react";
import { CodeGenContext, CodeGenContextProvider } from "../CodeGenContext";
import AutoField from "../AutoField";

export interface ListItemProps {
isListItem: boolean;
indexVariableName: string;
listName: string;
listStateName: string;
listStateSetter: string;
}

/**
* The list item can be nested or not (be part of an object).
* For non-nested items the `itemName` will have value "$", for nested items it will have its property name
*/
function getItemNameAndWithIsNested(name: string) {
const itemName = name.split(".").pop() ?? "$";
const isNested = itemName !== "$";
return { itemName, isNested };
}

/**
* This function can either return:
* `listName.$`
* `listName.${index}.itemName`
*/
export const getListItemName = (itemProps: ListItemProps, name: string) => {
const { itemName, isNested } = getItemNameAndWithIsNested(name);
return `\`${itemProps?.listName}${isNested ? `.$\{${itemProps?.indexVariableName}}.${itemName}` : `.$\{${itemProps?.indexVariableName}}`}\``;
};

/**
* This function can either return:
* `listStateName[index]`
* `listStateName[index].itemName.`
*/
export const getListItemValue = (itemProps: ListItemProps, name: string) => {
const { itemName, isNested } = getItemNameAndWithIsNested(name);
return `${itemProps?.listStateName}[${itemProps?.indexVariableName}]${isNested ? `.${itemName}` : ""}`;
};

/**
* This function can either return:
* `newValue => listStateSetter(s =>
* const newState = [...s];
* const newState[index] = newValue;
* return newState;
* );`
* `newValue => listStateSetter(s =>
* const newState = [...s];
* const newState[index].itemName = newValue;
* return newState;
* );`
*/
export const getListItemOnChange = (itemProps: ListItemProps, name: string, callback?: (value: string) => string) => {
const { itemName, isNested } = getItemNameAndWithIsNested(name);
return `newValue => ${itemProps?.listStateSetter}(s => { const newState = [...s]; newState[${itemProps?.indexVariableName}]${isNested ? `.${itemName}` : ""} = newValue; return ${callback ? callback("newState") : "newState"}; })`;
};

export interface Props {
codegenCtx: CodeGenContext;
uniformsContext: Context<any>;
fieldName: any;
itemProps: ListItemProps;
disabled?: boolean;
}

export const ListItemField: React.FC<Props> = ({ codegenCtx, uniformsContext, fieldName, itemProps, disabled }) => {
return (
<CodeGenContextProvider schema={uniformsContext.schema} codegenCtx={codegenCtx} uniformsCtx={uniformsContext}>
<AutoField key={fieldName} name={fieldName} disabled={disabled} itemProps={itemProps} />
</CodeGenContextProvider>
);
};

export default ListItemField;
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ export interface Props {
codegenCtx: CodeGenContext;
uniformsContext: Context<any>;
field: any;
itempProps: any;
itemProps: any;
disabled?: boolean;
}

export const NestedFieldInput: React.FC<Props> = ({ codegenCtx, uniformsContext, field, itempProps, disabled }) => {
export const NestedFieldInput: React.FC<Props> = ({ codegenCtx, uniformsContext, field, itemProps, disabled }) => {
return (
<CodeGenContextProvider schema={uniformsContext.schema} codegenCtx={codegenCtx} uniformsCtx={uniformsContext}>
<AutoField key={field} name={field} disabled={disabled} {...itempProps} />
<AutoField key={field} name={field} disabled={disabled} itemProps={itemProps} />
</CodeGenContextProvider>
);
};
Expand Down
Loading

0 comments on commit 53363e3

Please sign in to comment.