feat: migrate form builder to shadcn/ui + Huge Icons
- Replace @wordpress/components with shadcn Input, Button, Switch, Select, Textarea, Label - Replace @wordpress/icons with @hugeicons/core-free-icons and @hugeicons/react - Update fieldTypes config with Huge Icons component references - Migrate FormFieldOptions, FieldSettingsPanel, FormField, FormCanvas, FieldPalette
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hugeicons/core-free-icons": "^4.1.1",
|
||||||
"@hugeicons/react": "^1.1.6",
|
"@hugeicons/react": "^1.1.6",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
@@ -2413,6 +2414,12 @@
|
|||||||
"@hapi/hoek": "^9.0.0"
|
"@hapi/hoek": "^9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@hugeicons/core-free-icons": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-teqIBvPHl90ygIwKyJwTxOH8aNp1X1PjDTcMvLkEwdPxPD+8mssrZ5kXKIAJJFYPsz69a8LYQY0UPid4PAdavg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@hugeicons/react": {
|
"node_modules/@hugeicons/react": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@hugeicons/react/-/react-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@hugeicons/react/-/react-1.1.6.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"tw-animate-css": "^1.4.0"
|
"tw-animate-css": "^1.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hugeicons/core-free-icons": "^4.1.1",
|
||||||
"@hugeicons/react": "^1.1.6",
|
"@hugeicons/react": "^1.1.6",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon } from '@wordpress/icons';
|
import { HugeiconsIcon } from '@hugeicons/react';
|
||||||
import { FIELD_CATEGORIES, getFieldTypesByCategory } from '../../config/fieldTypes';
|
import { FIELD_CATEGORIES, getFieldTypesByCategory } from '../../config/fieldTypes';
|
||||||
import './FieldPalette.css';
|
import './FieldPalette.css';
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export default function FieldPalette({ onDragStart }) {
|
|||||||
onDragStart={(e) => handleDragStart(e, field.type)}
|
onDragStart={(e) => handleDragStart(e, field.type)}
|
||||||
title={field.label}
|
title={field.label}
|
||||||
>
|
>
|
||||||
<Icon icon={field.icon} />
|
<HugeiconsIcon icon={field.icon} size={20} />
|
||||||
<span>{ field.label }</span>
|
<span>{ field.label }</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { TextControl, CheckboxControl, SelectControl, TextareaControl } from '@wordpress/components';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select';
|
||||||
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { FIELD_TYPES } from '../../config/fieldTypes';
|
import { FIELD_TYPES } from '../../config/fieldTypes';
|
||||||
import FormFieldOptions from './FormFieldOptions';
|
import FormFieldOptions from './FormFieldOptions';
|
||||||
import './FieldSettingsPanel.css';
|
import './FieldSettingsPanel.css';
|
||||||
@@ -53,44 +57,54 @@ export default function FieldSettingsPanel({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="formipay-settings-content">
|
<div className="formipay-settings-content">
|
||||||
<TextControl
|
<div className="formipay-settings-field">
|
||||||
label={ __('Label', 'formipay') }
|
<Label>{ __('Label', 'formipay') }</Label>
|
||||||
value={field.label || ''}
|
<Input
|
||||||
onChange={(value) => handleChange('label', value)}
|
value={field.label || ''}
|
||||||
help={ __('The display label for this field', 'formipay') }
|
onChange={(e) => handleChange('label', e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<p className="formipay-settings-help">{ __('The display label for this field', 'formipay') }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TextControl
|
<div className="formipay-settings-field">
|
||||||
label={ __('Field ID', 'formipay') }
|
<Label>{ __('Field ID', 'formipay') }</Label>
|
||||||
value={field.field_id || ''}
|
<Input
|
||||||
onChange={(value) => handleChange('field_id', value)}
|
value={field.field_id || ''}
|
||||||
help={ __('Unique identifier for this field (used in form data)', 'formipay') }
|
onChange={(e) => handleChange('field_id', e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<p className="formipay-settings-help">{ __('Unique identifier for this field (used in form data)', 'formipay') }</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{ hasPlaceholder && (
|
{ hasPlaceholder && (
|
||||||
<TextControl
|
<div className="formipay-settings-field">
|
||||||
label={ __('Placeholder', 'formipay') }
|
<Label>{ __('Placeholder', 'formipay') }</Label>
|
||||||
value={field.placeholder || ''}
|
<Input
|
||||||
onChange={(value) => handleChange('placeholder', value)}
|
value={field.placeholder || ''}
|
||||||
/>
|
onChange={(e) => handleChange('placeholder', e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
{ hasDefaultValue && (
|
{ hasDefaultValue && (
|
||||||
<TextControl
|
<div className="formipay-settings-field">
|
||||||
label={ __('Default Value', 'formipay') }
|
<Label>{ __('Default Value', 'formipay') }</Label>
|
||||||
value={field.default_value || ''}
|
<Input
|
||||||
onChange={(value) => handleChange('default_value', value)}
|
value={field.default_value || ''}
|
||||||
/>
|
onChange={(e) => handleChange('default_value', e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
{ hasDescription && (
|
{ hasDescription && (
|
||||||
<TextareaControl
|
<div className="formipay-settings-field">
|
||||||
label={ __('Description', 'formipay') }
|
<Label>{ __('Description', 'formipay') }</Label>
|
||||||
value={field.description || ''}
|
<Textarea
|
||||||
onChange={(value) => handleChange('description', value)}
|
value={field.description || ''}
|
||||||
help={ __('Optional help text displayed below the field', 'formipay') }
|
onChange={(e) => handleChange('description', e.target.value)}
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
|
<p className="formipay-settings-help">{ __('Optional help text displayed below the field', 'formipay') }</p>
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
{ hasOptions && (
|
{ hasOptions && (
|
||||||
@@ -102,26 +116,36 @@ export default function FieldSettingsPanel({
|
|||||||
) }
|
) }
|
||||||
|
|
||||||
{ hasGridColumns && (
|
{ hasGridColumns && (
|
||||||
<SelectControl
|
<div className="formipay-settings-field">
|
||||||
label={ __('Option Grid Columns', 'formipay') }
|
<Label>{ __('Option Grid Columns', 'formipay') }</Label>
|
||||||
value={field.option_grid_columns || 1}
|
<Select
|
||||||
options={[
|
value={String(field.option_grid_columns || 1)}
|
||||||
{ label: __('1 Column', 'formipay'), value: 1 },
|
onValueChange={(value) => handleChange('option_grid_columns', parseInt(value))}
|
||||||
{ label: __('2 Columns', 'formipay'), value: 2 },
|
>
|
||||||
{ label: __('3 Columns', 'formipay'), value: 3 },
|
<SelectTrigger>
|
||||||
{ label: __('4 Columns', 'formipay'), value: 4 },
|
<SelectValue />
|
||||||
]}
|
</SelectTrigger>
|
||||||
onChange={(value) => handleChange('option_grid_columns', parseInt(value))}
|
<SelectContent>
|
||||||
/>
|
<SelectItem value="1">{__('1 Column', 'formipay')}</SelectItem>
|
||||||
|
<SelectItem value="2">{__('2 Columns', 'formipay')}</SelectItem>
|
||||||
|
<SelectItem value="3">{__('3 Columns', 'formipay')}</SelectItem>
|
||||||
|
<SelectItem value="4">{__('4 Columns', 'formipay')}</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
{ isRequired && (
|
{ isRequired && (
|
||||||
<CheckboxControl
|
<div className="formipay-settings-field formipay-settings-switch">
|
||||||
label={ __('Required Field', 'formipay') }
|
<div className="formipay-switch-row">
|
||||||
checked={field.is_required || false}
|
<Label>{ __('Required Field', 'formipay') }</Label>
|
||||||
onChange={(value) => handleChange('is_required', value)}
|
<Switch
|
||||||
help={ __('User must fill this field before submitting', 'formipay') }
|
checked={field.is_required || false}
|
||||||
/>
|
onCheckedChange={(value) => handleChange('is_required', value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="formipay-settings-help">{ __('User must fill this field before submitting', 'formipay') }</p>
|
||||||
|
</div>
|
||||||
) }
|
) }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon } from '@wordpress/icons';
|
import { Add01Icon } from '@hugeicons/react';
|
||||||
import plus from '@wordpress/icons/build/plus';
|
|
||||||
import { DEFAULT_FIELD_CONFIG, generateFieldId } from '../../config/fieldTypes';
|
import { DEFAULT_FIELD_CONFIG, generateFieldId } from '../../config/fieldTypes';
|
||||||
import FormField from './FormField';
|
import FormField from './FormField';
|
||||||
import './FormCanvas.css';
|
import './FormCanvas.css';
|
||||||
@@ -61,7 +60,7 @@ export default function FormCanvas({
|
|||||||
>
|
>
|
||||||
{fields.length === 0 ? (
|
{fields.length === 0 ? (
|
||||||
<div className="formipay-empty-state">
|
<div className="formipay-empty-state">
|
||||||
<Icon icon={plus()} size={48} />
|
<Add01Icon size={48} />
|
||||||
<p>
|
<p>
|
||||||
{ __('Drag fields from the palette to build your form', 'formipay') }
|
{ __('Drag fields from the palette to build your form', 'formipay') }
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon } from '@wordpress/icons';
|
import { ArrowUp01Icon, ArrowDown01Icon, Delete02Icon } from '@hugeicons/react';
|
||||||
import chevronUp from '@wordpress/icons/build/chevron-up';
|
|
||||||
import chevronDown from '@wordpress/icons/build/chevron-down';
|
|
||||||
import trash from '@wordpress/icons/build/trash';
|
|
||||||
import { FIELD_TYPES } from '../../config/fieldTypes';
|
import { FIELD_TYPES } from '../../config/fieldTypes';
|
||||||
import './FormField.css';
|
import './FormField.css';
|
||||||
|
|
||||||
@@ -52,7 +49,7 @@ export default function FormField({
|
|||||||
disabled={!onMoveUp}
|
disabled={!onMoveUp}
|
||||||
title={ __('Move Up', 'formipay') }
|
title={ __('Move Up', 'formipay') }
|
||||||
>
|
>
|
||||||
<Icon icon={chevronUp()} size={16} />
|
<ArrowUp01Icon size={16} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -61,7 +58,7 @@ export default function FormField({
|
|||||||
disabled={!onMoveDown}
|
disabled={!onMoveDown}
|
||||||
title={ __('Move Down', 'formipay') }
|
title={ __('Move Down', 'formipay') }
|
||||||
>
|
>
|
||||||
<Icon icon={chevronDown()} size={16} />
|
<ArrowDown01Icon size={16} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -69,7 +66,7 @@ export default function FormField({
|
|||||||
onClick={(e) => { e.stopPropagation(); onDelete?.(); }}
|
onClick={(e) => { e.stopPropagation(); onDelete?.(); }}
|
||||||
title={ __('Delete', 'formipay') }
|
title={ __('Delete', 'formipay') }
|
||||||
>
|
>
|
||||||
<Icon icon={trash()} size={16} />
|
<Delete02Icon size={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { TextControl, Button } from '@wordpress/components';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Icon as WPIcon } from '@wordpress/icons';
|
import { Input } from '@/components/ui/input';
|
||||||
import plus from '@wordpress/icons/build/plus';
|
import { Label } from '@/components/ui/label';
|
||||||
import trash from '@wordpress/icons/build/trash';
|
import { Add01Icon, Delete02Icon } from '@hugeicons/react';
|
||||||
|
|
||||||
export default function FormFieldOptions({ options = [], onChange, fieldType }) {
|
export default function FormFieldOptions({ options = [], onChange, fieldType }) {
|
||||||
const handleAddOption = () => {
|
const handleAddOption = () => {
|
||||||
@@ -46,10 +46,10 @@ export default function FormFieldOptions({ options = [], onChange, fieldType })
|
|||||||
</label>
|
</label>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="compact"
|
size="sm"
|
||||||
onClick={handleAddOption}
|
onClick={handleAddOption}
|
||||||
icon={<WPIcon icon={plus()} size={16} />}
|
|
||||||
>
|
>
|
||||||
|
<Add01Icon size={16} />
|
||||||
{ __('Add Option', 'formipay') }
|
{ __('Add Option', 'formipay') }
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,38 +58,45 @@ export default function FormFieldOptions({ options = [], onChange, fieldType })
|
|||||||
{options.map((option, index) => (
|
{options.map((option, index) => (
|
||||||
<div key={index} className="formipay-option-item">
|
<div key={index} className="formipay-option-item">
|
||||||
<div className="formipay-option-fields">
|
<div className="formipay-option-fields">
|
||||||
<TextControl
|
<div className="formipay-option-field">
|
||||||
label={__('Label', 'formipay')}
|
<Label>{__('Label', 'formipay')}</Label>
|
||||||
value={option.label || ''}
|
<Input
|
||||||
onChange={(value) => handleUpdateOption(index, { label: value })}
|
value={option.label || ''}
|
||||||
placeholder={__('Option label', 'formipay')}
|
onChange={(e) => handleUpdateOption(index, { label: e.target.value })}
|
||||||
/>
|
placeholder={__('Option label', 'formipay')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TextControl
|
<div className="formipay-option-field">
|
||||||
label={__('Value', 'formipay')}
|
<Label>{__('Value', 'formipay')}</Label>
|
||||||
value={option.value || ''}
|
<Input
|
||||||
onChange={(value) => handleUpdateOption(index, { value: value })}
|
value={option.value || ''}
|
||||||
placeholder={__('Optional custom value', 'formipay')}
|
onChange={(e) => handleUpdateOption(index, { value: e.target.value })}
|
||||||
/>
|
placeholder={__('Optional custom value', 'formipay')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TextControl
|
<div className="formipay-option-field">
|
||||||
label={__('Amount', 'formipay')}
|
<Label>{__('Amount', 'formipay')}</Label>
|
||||||
type="number"
|
<Input
|
||||||
value={option.amount || ''}
|
type="number"
|
||||||
onChange={(value) => handleUpdateOption(index, { amount: value })}
|
value={option.amount || ''}
|
||||||
placeholder={__('0', 'formipay')}
|
onChange={(e) => handleUpdateOption(index, { amount: e.target.value })}
|
||||||
step="0.01"
|
placeholder={__('0', 'formipay')}
|
||||||
/>
|
step="0.01"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="formipay-option-actions">
|
<div className="formipay-option-actions">
|
||||||
<Button
|
<Button
|
||||||
variant="tertiary"
|
variant="ghost"
|
||||||
size="compact"
|
size="icon"
|
||||||
onClick={() => handleDeleteOption(index)}
|
onClick={() => handleDeleteOption(index)}
|
||||||
icon={<WPIcon icon={trash()} size={16} />}
|
title={__('Delete Option', 'formipay')}
|
||||||
label={__('Delete Option', 'formipay')}
|
>
|
||||||
/>
|
<Delete02Icon size={16} />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -2,23 +2,42 @@
|
|||||||
* Form Field Type Definitions
|
* Form Field Type Definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
TextFontIcon,
|
||||||
|
Link01Icon,
|
||||||
|
Mail01Icon,
|
||||||
|
TelephoneIcon,
|
||||||
|
HashtagIcon,
|
||||||
|
Calendar01Icon,
|
||||||
|
Calendar03Icon,
|
||||||
|
PaintBoardIcon,
|
||||||
|
DropdownFieldTypeIcon,
|
||||||
|
CheckmarkSquare01Icon,
|
||||||
|
RadioButtonIcon,
|
||||||
|
EyeIcon,
|
||||||
|
ParagraphIcon,
|
||||||
|
MinusSignIcon,
|
||||||
|
NextIcon,
|
||||||
|
Globe02Icon,
|
||||||
|
} from '@hugeicons/core-free-icons';
|
||||||
|
|
||||||
export const FIELD_TYPES = {
|
export const FIELD_TYPES = {
|
||||||
text: { label: 'Text', icon: 'text', category: 'input' },
|
text: { label: 'Text', icon: TextFontIcon, category: 'input' },
|
||||||
url: { label: 'URL', icon: 'link', category: 'input' },
|
url: { label: 'URL', icon: Link01Icon, category: 'input' },
|
||||||
email: { label: 'Email', icon: 'email', category: 'input' },
|
email: { label: 'Email', icon: Mail01Icon, category: 'input' },
|
||||||
tel: { label: 'Telephone', icon: 'phone', category: 'input' },
|
tel: { label: 'Telephone', icon: TelephoneIcon, category: 'input' },
|
||||||
number: { label: 'Number', icon: 'number', category: 'input' },
|
number: { label: 'Number', icon: HashtagIcon, category: 'input' },
|
||||||
date: { label: 'Date', icon: 'calendar', category: 'input' },
|
date: { label: 'Date', icon: Calendar01Icon, category: 'input' },
|
||||||
datetime: { label: 'Date & Time', icon: 'calendar-alt', category: 'input' },
|
datetime: { label: 'Date & Time', icon: Calendar03Icon, category: 'input' },
|
||||||
color: { label: 'Color', icon: 'art', category: 'input' },
|
color: { label: 'Color', icon: PaintBoardIcon, category: 'input' },
|
||||||
select: { label: 'Select Dropdown', icon: 'list-view', category: 'choice' },
|
select: { label: 'Select Dropdown', icon: DropdownFieldTypeIcon, category: 'choice' },
|
||||||
checkbox: { label: 'Checkbox', icon: 'checkbox', category: 'choice' },
|
checkbox: { label: 'Checkbox', icon: CheckmarkSquare01Icon, category: 'choice' },
|
||||||
radio: { label: 'Radio', icon: 'radio', category: 'choice' },
|
radio: { label: 'Radio', icon: RadioButtonIcon, category: 'choice' },
|
||||||
hidden: { label: 'Hidden', icon: 'hidden', category: 'advanced' },
|
hidden: { label: 'Hidden', icon: EyeIcon, category: 'advanced' },
|
||||||
textarea: { label: 'Textarea', icon: 'document', category: 'input' },
|
textarea: { label: 'Textarea', icon: ParagraphIcon, category: 'input' },
|
||||||
divider: { label: 'Divider', icon: 'minus', category: 'layout' },
|
divider: { label: 'Divider', icon: MinusSignIcon, category: 'layout' },
|
||||||
page_break: { label: 'Page Break', icon: 'page-break', category: 'layout' },
|
page_break: { label: 'Page Break', icon: NextIcon, category: 'layout' },
|
||||||
country_list: { label: 'Preset: Country List', icon: 'globe', category: 'preset' },
|
country_list: { label: 'Preset: Country List', icon: Globe02Icon, category: 'preset' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FIELD_CATEGORIES = {
|
export const FIELD_CATEGORIES = {
|
||||||
|
|||||||
Reference in New Issue
Block a user