PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.1.3
JetFormBuilder — Dynamic Blocks Form Builder v3.1.3
3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / assets / src / package / repeater / components / RepeaterWithState.js
jetformbuilder / assets / src / package / repeater / components Last commit date
RepeaterWithState.js 2 years ago repeater.add.new.js 2 years ago repeater.add.or.js 2 years ago repeater.js 2 years ago repeater.state.js 2 years ago safe.delete.toggle.js 2 years ago
RepeaterWithState.js
308 lines
1 const { __ } = wp.i18n;
2
3 const {
4 Button,
5 ButtonGroup,
6 Card,
7 CardBody,
8 CardHeader,
9 ToggleControl,
10 Flex,
11 FlexItem,
12 } = wp.components;
13
14 const {
15 useState,
16 useEffect,
17 } = wp.element;
18
19 function RepeaterWithState( {
20 children,
21 ItemHeading,
22 repeaterClasses = [],
23 repeaterItemClasses = [],
24 newItem,
25 addNewButtonLabel = 'Add New',
26 items = [],
27 isSaveAction,
28 onSaveItems,
29 onUnMount,
30 onAddNewItem,
31 onRemoveItem,
32 help = {
33 helpSource: {},
34 helpVisible: () => false,
35 helpKey: '',
36 },
37 additionalControls = null,
38 } ) {
39
40 const classNames = [ 'jet-form-builder__repeater-component', ...repeaterClasses ].join( ' ' );
41 const itemClassNames = [ 'jet-form-builder__repeater-component-item', ...repeaterItemClasses ].join( ' ' );
42
43 const parsedItems = () => {
44 if ( items && items.length > 0 ) {
45 return items.map( item => {
46 item.__visible = false;
47
48 return item;
49 } );
50 } else {
51 return [ {
52 ...newItem,
53 __visible: true,
54 } ];
55 }
56 }
57
58 const [ itemsData, setItemsData ] = useState( [] );
59
60 useEffect( () => {
61 setItemsData( parsedItems() );
62 }, [] );
63
64 const [ isSafeDeleting, setSafeDeleting ] = useState( true );
65
66 const changeCurrentItem = ( valueToSet, index ) => {
67 setItemsData( prev => {
68 const prevClone = JSON.parse( JSON.stringify( prev ) );
69
70 prevClone[ index ] = {
71 ...prev[ index ],
72 ...valueToSet,
73 };
74 return prevClone;
75 } );
76 };
77
78 const onSaveDeleting = index => {
79 return confirm( __( `Are you sure you want to remove item ${ index + 1 }?`, 'jet-form-builder' ) );
80 }
81
82 const removeOption = ( index ) => {
83 if (
84 isSafeDeleting && ! onSaveDeleting( index ) ||
85 onRemoveItem && ! onRemoveItem( index, itemsData )
86 ) {
87 return;
88 }
89
90 setItemsData( prev => {
91 const prevClone = JSON.parse( JSON.stringify( prev ) );
92 prevClone.splice( index, 1 );
93
94 return prevClone;
95 } );
96 }
97
98 const addNewItem = ( value ) => {
99 if ( onAddNewItem ) {
100 onAddNewItem( value, itemsData );
101 }
102 setItemsData( prev => [ ...prev, {
103 ...value,
104 __visible: true,
105 } ] );
106 }
107
108 const cloneItem = ( index ) => {
109 setItemsData( prev => {
110 const prevClone = JSON.parse( JSON.stringify( prev ) );
111 const [ before, after ] = [ prevClone.slice( 0, index + 1 ), prevClone.slice( index + 1 ) ];
112
113 return [ ...before, prevClone[ index ], ...after ];
114 } );
115 }
116
117 const moveRepeaterItem = ( { oldIndex, newIndex } ) => {
118 setItemsData( prev => {
119 const prevClone = JSON.parse( JSON.stringify( prev ) );
120
121 [ prevClone[ newIndex ], prevClone[ oldIndex ] ] = [ prevClone[ oldIndex ], prevClone[ newIndex ] ];
122
123 return prevClone;
124 } );
125 }
126
127 const moveUp = ( index ) => {
128 moveRepeaterItem( { oldIndex: index, newIndex: index - 1 } );
129 }
130 const moveDown = ( index ) => {
131 moveRepeaterItem( { oldIndex: index, newIndex: index + 1 } );
132 }
133
134 const isDisabledEnd = ( index ) => {
135 return ! ( index < itemsData.length - 1 );
136 }
137 /*
138
139 */
140
141 const toggleVisible = index => {
142 setItemsData( prev => {
143 const prevClone = JSON.parse( JSON.stringify( prev ) );
144 prevClone[ index ].__visible = ! ( prevClone[ index ].__visible );
145
146 return prevClone;
147 } );
148 };
149
150 useEffect( () => {
151 if ( true === isSaveAction ) {
152 for ( const itemsDataKey in itemsData ) {
153 for ( const itemKey in itemsData[ itemsDataKey ] ) {
154 if ( itemKey.startsWith( '__' ) ) {
155 delete itemsData[ itemsDataKey ][ itemKey ];
156 }
157 }
158 }
159 onSaveItems( itemsData );
160 onUnMount();
161 } else if ( false === isSaveAction ) {
162 onUnMount();
163 }
164 }, [ isSaveAction ] );
165
166 const getRepeaterItemId = index => `jet-form-builder-repeater__item_${ index }`;
167
168 const {
169 helpSource,
170 helpVisible,
171 helpKey,
172 } = help;
173
174 const isVisibleHelp = helpVisible( itemsData ) && helpSource && helpSource[ helpKey ];
175
176 return <div
177 className={ classNames }
178 key={ 'jet-form-builder-repeater' }
179 >
180 { isVisibleHelp && <p>{ helpSource[ helpKey ].label }</p> }
181
182 { 0 < itemsData.length && <>
183 <Flex>
184 <FlexItem>
185 <ToggleControl
186 label={ __( 'Safe deleting', 'jet-form-builder' ) }
187 checked={ isSafeDeleting }
188 onChange={ setSafeDeleting }
189 />
190 </FlexItem>
191 <FlexItem style={ {
192 flex: 'unset',
193 marginBottom: '1em',
194 } }>
195 {/*<ButtonGroup>
196 <Button
197 isSecondary
198 onClick={ () => {
199 } }
200 >
201 { __( 'Copy items to clipboard', 'jet-from-builder' ) }
202 </Button>
203 <Button
204 isSecondary
205 onClick={ () => {
206 } }
207 >
208 { __( 'Import items from clipboard', 'jet-from-builder' ) }
209 </Button>
210 </ButtonGroup>*/ }
211 </FlexItem>
212 </Flex>
213 </> }
214 { additionalControls }
215 { itemsData.map( ( currentItem, index ) => <Card
216 elevation={ 2 }
217 className={ itemClassNames }
218 key={ getRepeaterItemId( index ) }
219 >
220 <CardHeader className={ 'repeater__item__header' }>
221 <div className='repeater-item__left-heading'>
222 <ButtonGroup className={ 'repeater-action-buttons' }>
223 <Button
224 isSmall
225 icon={ currentItem.__visible ? 'no-alt' : 'edit' }
226 onClick={ () => toggleVisible( index ) }
227 className={ 'repeater-action-button' }
228 />
229 <Button
230 isSmall
231 isSecondary
232 disabled={ ! Boolean( index ) }
233 icon={ 'arrow-up-alt2' }
234 onClick={ () => moveUp( index ) }
235 className={ 'repeater-action-button' }
236 />
237 <Button
238 isSmall
239 isSecondary
240 disabled={ isDisabledEnd( index ) }
241 icon={ 'arrow-down-alt2' }
242 onClick={ () => moveDown( index ) }
243 className={ 'repeater-action-button' }
244 />
245 </ButtonGroup>
246 <span className={ 'repeater-item-title' }>
247 { ItemHeading && <ItemHeading
248 currentItem={ currentItem }
249 index={ index }
250 changeCurrentItem={ data => changeCurrentItem( data, index ) }
251 /> }
252 { ! ItemHeading && `#${ index + 1 }` }
253 </span>
254 </div>
255 <ButtonGroup>
256 <Button
257 isSmall
258 isSecondary
259 onClick={ () => cloneItem( index ) }
260 >
261 { __( 'Clone', 'jet-form-builder' ) }
262 </Button>
263 <Button
264 isSmall
265 isSecondary
266 isDestructive
267 onClick={ () => removeOption( index ) }
268 >
269 { __( 'Delete', 'jet-form-builder' ) }
270 </Button>
271 </ButtonGroup>
272 </CardHeader>
273 { currentItem.__visible && <CardBody
274 className={ 'repeater-item__content' }
275 >
276 { children && <React.Fragment
277 key={ `repeater-component__item_${ index }` }
278 >
279 { 'function' === typeof children && children( {
280 currentItem,
281 changeCurrentItem: data => changeCurrentItem( data, index ),
282 currentIndex: index,
283 } ) }
284 { 'function' !== typeof children && children }
285 </React.Fragment> }
286 { ! children && 'Set up your Repeater Template, please.' }
287 </CardBody> }
288 </Card> ) }
289 { 1 < itemsData.length && <>
290 { additionalControls }
291 <ToggleControl
292 className='jet-control-clear'
293 label={ __( 'Safe deleting' ) }
294 checked={ isSafeDeleting }
295 onChange={ setSafeDeleting }
296 />
297 </> }
298 <Button
299 isSecondary
300 onClick={ () => addNewItem( newItem ) }
301 >
302 { addNewButtonLabel }
303 </Button>
304 </div>;
305
306 }
307
308 export default RepeaterWithState;