@siratik/wp-component-library 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -0
- package/README.md +43 -5
- package/components/attachment-image/README.md +118 -0
- package/components/attachment-image/index.js +105 -0
- package/components/attributes-panel/README.md +92 -0
- package/components/attributes-panel/index.js +324 -0
- package/components/badge/README.md +61 -0
- package/components/badge/index.js +105 -0
- package/components/block-appender/README.md +158 -0
- package/components/block-appender/index.js +135 -0
- package/components/block-extension/index.js +214 -0
- package/components/business-hours/SpecialHoursControl.js +211 -0
- package/components/business-hours/WeeklyHoursControl.js +419 -0
- package/components/business-hours/index.js +8 -0
- package/components/button/README.md +172 -0
- package/components/button/index.js +84 -0
- package/components/captcha-placeholder/CloudflareTurnstile.js +111 -0
- package/components/captcha-placeholder/GoogleRecaptcha.js +132 -0
- package/components/captcha-placeholder/HCaptcha.js +99 -0
- package/components/captcha-placeholder/README.md +228 -0
- package/components/captcha-placeholder/index.js +39 -0
- package/components/captcha-placeholder/styles.js +33 -0
- package/components/geocoder/AddressEntryControl.js +390 -0
- package/components/geocoder/GeocoderSearchControl.js +353 -0
- package/components/geocoder/countryOptions.js +66 -0
- package/components/geocoder/geocoder.js +400 -0
- package/components/geocoder/index.js +16 -0
- package/components/icon-picker/Icon.js +53 -0
- package/components/icon-picker/IconPicker.js +70 -0
- package/components/icon-picker/IconPickerModal.js +474 -0
- package/components/icon-picker/IconPickerToolbarButton.js +60 -0
- package/components/icon-picker/InlineIconPicker.js +92 -0
- package/components/icon-picker/api.js +181 -0
- package/components/icon-picker/config-bootstrap.js +108 -0
- package/components/icon-picker/hooks.js +157 -0
- package/components/icon-picker/index.js +39 -0
- package/components/icon-picker/load-script.js +33 -0
- package/components/icon-picker/loaders.js +79 -0
- package/components/icon-picker/store.js +223 -0
- package/components/icon-picker/styles.js +232 -0
- package/components/icon-picker/utils.js +138 -0
- package/components/layout/README.md +268 -0
- package/components/link-tools-panel/README.md +49 -0
- package/components/link-tools-panel/index.js +198 -0
- package/components/link-tools-panel/styles.js +90 -0
- package/components/media/README.md +288 -0
- package/components/media/SettingsMediaUpload.js +187 -0
- package/components/media/editor-media-upload.js +65 -0
- package/components/media/image-controls.js +196 -0
- package/components/media/index.js +13 -0
- package/components/media/inspector-media-upload.js +290 -0
- package/components/media/toolbar-media-upload.js +53 -0
- package/components/media/utils/aspect-ratios.js +118 -0
- package/components/meta/MetaPanel.js +75 -0
- package/components/meta/README.md +46 -0
- package/components/meta/index.js +6 -0
- package/components/meta/meta-gated-editor.js +282 -0
- package/components/query/README.md +340 -0
- package/components/query/cardTemplatePartPanel.js +69 -0
- package/components/query/cardTemplatePartSelect.js +165 -0
- package/components/query/cardTemplatePartUtils.js +119 -0
- package/components/query/columnCountControl.js +63 -0
- package/components/query/displayTypeSelect.js +52 -0
- package/components/query/examples.js +166 -0
- package/components/query/hideCurrentPostControl.js +47 -0
- package/components/query/index.js +20 -0
- package/components/query/manualPostSelector.js +125 -0
- package/components/query/manualTermSelector.js +141 -0
- package/components/query/orderBySelect.js +95 -0
- package/components/query/postTypeSelect.js +67 -0
- package/components/query/postsPerPageControl.js +51 -0
- package/components/query/selectionModeControl.js +61 -0
- package/components/query/taxonomySelect.js +139 -0
- package/components/query/utils.js +75 -0
- package/components/repeater/README.md +159 -0
- package/components/repeater/index.js +829 -0
- package/components/segmented-bar/README.md +30 -0
- package/components/segmented-bar/index.js +68 -0
- package/components/sortable-select/README.md +153 -0
- package/components/sortable-select/custom-sortable-token.js +127 -0
- package/components/sortable-select/index.js +693 -0
- package/components/style-picker/README.md +36 -0
- package/components/style-picker/index.js +92 -0
- package/components/variable-field/README.md +299 -0
- package/components/variable-field/index.js +592 -0
- package/components/variable-field/value-to-chip-html.js +38 -0
- package/components/variable-inserter/README.md +68 -0
- package/components/variable-inserter/index.js +168 -0
- package/constants/statusColors.js +1 -1
- package/editor.js +16 -0
- package/index.js +127 -0
- package/package.json +21 -13
- package/utils/kitaris-localize.js +128 -0
- package/utils/slug-aware-color-setter.js +27 -0
- package/build/index.asset.php +0 -20
- package/build/index.js +0 -1242
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StylePicker Component
|
|
3
|
+
*
|
|
4
|
+
* One option out of a few, shown like core's block Styles panel: a two-column
|
|
5
|
+
* grid of buttons, the chosen one filled. For a block setting that reads as a
|
|
6
|
+
* style but isn't a block style (an attribute such as a button variant).
|
|
7
|
+
*
|
|
8
|
+
* It renders core's own markup and classes (block-editor-block-styles__*), so
|
|
9
|
+
* the block editor's styles apply and it matches the Styles panel exactly.
|
|
10
|
+
*
|
|
11
|
+
* @package WPComponentLibrary
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
BaseControl,
|
|
16
|
+
Button,
|
|
17
|
+
__experimentalTruncate as Truncate,
|
|
18
|
+
} from "@wordpress/components";
|
|
19
|
+
import { useId } from "@wordpress/element";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {Object} props
|
|
23
|
+
* @param {string} props.label Control label.
|
|
24
|
+
* @param {string} [props.help] Help text.
|
|
25
|
+
* @param {string} props.value The chosen option's value.
|
|
26
|
+
* @param {Array<{value: string, label: string}>} props.options Options, in order.
|
|
27
|
+
* @param {Function} props.onChange Receives the chosen value.
|
|
28
|
+
* @param {boolean} [props.hideLabelFromVision] Keep the label for screen readers only.
|
|
29
|
+
* @param {string} [props.className]
|
|
30
|
+
* @return {WPElement} The picker.
|
|
31
|
+
*/
|
|
32
|
+
export function StylePicker({
|
|
33
|
+
label,
|
|
34
|
+
help,
|
|
35
|
+
value,
|
|
36
|
+
options,
|
|
37
|
+
onChange,
|
|
38
|
+
hideLabelFromVision = false,
|
|
39
|
+
className,
|
|
40
|
+
}) {
|
|
41
|
+
const labelId = useId();
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<BaseControl
|
|
45
|
+
__nextHasNoMarginBottom
|
|
46
|
+
help={help}
|
|
47
|
+
className={className}
|
|
48
|
+
>
|
|
49
|
+
{hideLabelFromVision ? (
|
|
50
|
+
<span id={labelId} className="screen-reader-text">
|
|
51
|
+
{label}
|
|
52
|
+
</span>
|
|
53
|
+
) : (
|
|
54
|
+
<BaseControl.VisualLabel>
|
|
55
|
+
<span id={labelId}>{label}</span>
|
|
56
|
+
</BaseControl.VisualLabel>
|
|
57
|
+
)}
|
|
58
|
+
<div
|
|
59
|
+
className="block-editor-block-styles"
|
|
60
|
+
role="group"
|
|
61
|
+
aria-labelledby={labelId}
|
|
62
|
+
>
|
|
63
|
+
<div className="block-editor-block-styles__variants">
|
|
64
|
+
{options.map((option) => {
|
|
65
|
+
const isActive = option.value === value;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Button
|
|
69
|
+
key={option.value}
|
|
70
|
+
__next40pxDefaultSize
|
|
71
|
+
variant="secondary"
|
|
72
|
+
className={`block-editor-block-styles__item${
|
|
73
|
+
isActive ? " is-active" : ""
|
|
74
|
+
}`}
|
|
75
|
+
label={option.label}
|
|
76
|
+
aria-current={isActive}
|
|
77
|
+
onClick={() => onChange(option.value)}
|
|
78
|
+
>
|
|
79
|
+
<Truncate
|
|
80
|
+
numberOfLines={1}
|
|
81
|
+
className="block-editor-block-styles__item-text"
|
|
82
|
+
>
|
|
83
|
+
{option.label}
|
|
84
|
+
</Truncate>
|
|
85
|
+
</Button>
|
|
86
|
+
);
|
|
87
|
+
})}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</BaseControl>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# VariableField Component
|
|
2
|
+
|
|
3
|
+
An advanced input field component with autocomplete functionality for inserting variables using the @ symbol trigger. Perfect for template fields, email composers, or any input that needs variable interpolation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **@ Symbol Triggered Autocomplete** - Type @ to show variable suggestions
|
|
8
|
+
- **Smart Filtering** - Real-time filtering as you type after @
|
|
9
|
+
- **Keyboard Navigation** - Full keyboard support with arrow keys, Enter, and Escape
|
|
10
|
+
- **Mouse Support** - Click to select, hover to highlight
|
|
11
|
+
- **Cursor-Aware Replacement** - Replaces text at cursor position precisely
|
|
12
|
+
- **Flexible Input Types** - Supports both text input and textarea
|
|
13
|
+
- **Customizable Options** - Define your own variable structure
|
|
14
|
+
- **Accessibility** - Screen reader compatible with proper ARIA attributes
|
|
15
|
+
- **Click Outside Detection** - Automatically closes suggestions when clicking elsewhere
|
|
16
|
+
- **Ref Forwarding** - Exposes focus method to parent components
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```jsx
|
|
21
|
+
import { VariableField } from '@siratik/wp-component-library/components/variable-field';
|
|
22
|
+
|
|
23
|
+
// Basic usage with simple options
|
|
24
|
+
<VariableField
|
|
25
|
+
value={content}
|
|
26
|
+
onChange={setContent}
|
|
27
|
+
options={[
|
|
28
|
+
{ label: 'User Name', value: '{user_name}' },
|
|
29
|
+
{ label: 'User Email', value: '{user_email}' },
|
|
30
|
+
{ label: 'Current Date', value: '{date}' },
|
|
31
|
+
{ label: 'Site Title', value: '{site_title}' }
|
|
32
|
+
]}
|
|
33
|
+
placeholder="Type @ to insert variables"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
// With custom option structure
|
|
37
|
+
<VariableField
|
|
38
|
+
value={template}
|
|
39
|
+
onChange={setTemplate}
|
|
40
|
+
options={customVariables}
|
|
41
|
+
getOptionLabel={(option) => option.displayName}
|
|
42
|
+
getOptionValue={(option) => option.code}
|
|
43
|
+
label="Email Template"
|
|
44
|
+
help="Use @ to insert merge tags"
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
// As textarea with advanced options
|
|
48
|
+
<VariableField
|
|
49
|
+
value={message}
|
|
50
|
+
onChange={setMessage}
|
|
51
|
+
options={messageVariables}
|
|
52
|
+
inputType="textarea"
|
|
53
|
+
label="Message Body"
|
|
54
|
+
placeholder="Compose your message. Type @ for variables."
|
|
55
|
+
className="message-composer"
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Props
|
|
60
|
+
|
|
61
|
+
| Prop | Type | Default | Description |
|
|
62
|
+
|------|------|---------|-------------|
|
|
63
|
+
| `value` | `string` | `""` | Current input value |
|
|
64
|
+
| `onChange` | `function` | Required | Value change handler |
|
|
65
|
+
| `options` | `array` | `[]` | Available variables for autocomplete |
|
|
66
|
+
| `getOptionLabel` | `function` | `(opt) => opt.label` | Extract display label from option |
|
|
67
|
+
| `getOptionValue` | `function` | `(opt) => opt.value` | Extract value from option |
|
|
68
|
+
| `placeholder` | `string` | - | Input placeholder text |
|
|
69
|
+
| `label` | `string` | - | Field label |
|
|
70
|
+
| `help` | `string` | - | Help text displayed below input |
|
|
71
|
+
| `isDisabled` | `boolean` | `false` | Disable the input |
|
|
72
|
+
| `inputType` | `string` | `"text"` | Input type: "text" or "textarea" |
|
|
73
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
74
|
+
|
|
75
|
+
## Option Structure
|
|
76
|
+
|
|
77
|
+
Options can be simple or complex objects:
|
|
78
|
+
|
|
79
|
+
### Simple Structure
|
|
80
|
+
```jsx
|
|
81
|
+
const simpleOptions = [
|
|
82
|
+
{ label: 'First Name', value: '{first_name}' },
|
|
83
|
+
{ label: 'Last Name', value: '{last_name}' }
|
|
84
|
+
];
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Custom Structure
|
|
88
|
+
```jsx
|
|
89
|
+
const customOptions = [
|
|
90
|
+
{
|
|
91
|
+
displayName: 'Customer Name',
|
|
92
|
+
code: '{{customer.name}}',
|
|
93
|
+
category: 'Customer Data',
|
|
94
|
+
description: 'Full name of the customer'
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// With custom getters
|
|
99
|
+
<VariableField
|
|
100
|
+
options={customOptions}
|
|
101
|
+
getOptionLabel={(opt) => opt.displayName}
|
|
102
|
+
getOptionValue={(opt) => opt.code}
|
|
103
|
+
/>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Autocomplete Behavior
|
|
107
|
+
|
|
108
|
+
1. **Trigger** - Type @ anywhere in the input
|
|
109
|
+
2. **Filter** - Continue typing to filter suggestions
|
|
110
|
+
3. **Navigate** - Use ↑↓ arrow keys or mouse
|
|
111
|
+
4. **Select** - Press Enter or click to insert
|
|
112
|
+
5. **Cancel** - Press Escape or click outside
|
|
113
|
+
|
|
114
|
+
The selected variable replaces the @word at the cursor position.
|
|
115
|
+
|
|
116
|
+
## Keyboard Shortcuts
|
|
117
|
+
|
|
118
|
+
| Key | Action |
|
|
119
|
+
|-----|--------|
|
|
120
|
+
| `@` | Open autocomplete suggestions |
|
|
121
|
+
| `↑` `↓` | Navigate through suggestions |
|
|
122
|
+
| `Enter` | Select highlighted suggestion |
|
|
123
|
+
| `Escape` | Close suggestions |
|
|
124
|
+
| `Tab` | Close suggestions and move focus |
|
|
125
|
+
|
|
126
|
+
## Examples
|
|
127
|
+
|
|
128
|
+
### Email Template Builder
|
|
129
|
+
```jsx
|
|
130
|
+
function EmailTemplateBuilder() {
|
|
131
|
+
const [subject, setSubject] = useState('');
|
|
132
|
+
const [body, setBody] = useState('');
|
|
133
|
+
|
|
134
|
+
const emailVariables = [
|
|
135
|
+
{ label: 'Recipient Name', value: '{{recipient.name}}' },
|
|
136
|
+
{ label: 'Recipient Email', value: '{{recipient.email}}' },
|
|
137
|
+
{ label: 'Sender Name', value: '{{sender.name}}' },
|
|
138
|
+
{ label: 'Company Name', value: '{{company.name}}' },
|
|
139
|
+
{ label: 'Unsubscribe Link', value: '{{unsubscribe_url}}' },
|
|
140
|
+
{ label: 'Current Year', value: '{{current_year}}' }
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div>
|
|
145
|
+
<VariableField
|
|
146
|
+
label="Subject Line"
|
|
147
|
+
value={subject}
|
|
148
|
+
onChange={setSubject}
|
|
149
|
+
options={emailVariables}
|
|
150
|
+
placeholder="Email subject..."
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
<VariableField
|
|
154
|
+
label="Email Body"
|
|
155
|
+
value={body}
|
|
156
|
+
onChange={setBody}
|
|
157
|
+
options={emailVariables}
|
|
158
|
+
inputType="textarea"
|
|
159
|
+
placeholder="Compose your email. Type @ to add variables."
|
|
160
|
+
help="Variables will be replaced with actual values when sending"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Dynamic Form Field
|
|
168
|
+
```jsx
|
|
169
|
+
function DynamicFormField() {
|
|
170
|
+
const [formula, setFormula] = useState('');
|
|
171
|
+
|
|
172
|
+
const fieldVariables = [
|
|
173
|
+
{ label: 'Field A', value: '[field_a]' },
|
|
174
|
+
{ label: 'Field B', value: '[field_b]' },
|
|
175
|
+
{ label: 'Total', value: '[total]' },
|
|
176
|
+
{ label: 'Tax Rate', value: '[tax_rate]' },
|
|
177
|
+
{ label: 'Subtotal', value: '[subtotal]' }
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<VariableField
|
|
182
|
+
label="Calculation Formula"
|
|
183
|
+
value={formula}
|
|
184
|
+
onChange={setFormula}
|
|
185
|
+
options={fieldVariables}
|
|
186
|
+
placeholder="e.g., [field_a] + [field_b] * [tax_rate]"
|
|
187
|
+
help="Create formulas using @ to reference other fields"
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Chat Message Composer
|
|
194
|
+
```jsx
|
|
195
|
+
function ChatComposer() {
|
|
196
|
+
const messageRef = useRef();
|
|
197
|
+
const [message, setMessage] = useState('');
|
|
198
|
+
|
|
199
|
+
const quickResponses = [
|
|
200
|
+
{ label: 'Greeting', value: 'Hello! How can I help you today?' },
|
|
201
|
+
{ label: 'Thank You', value: 'Thank you for contacting us!' },
|
|
202
|
+
{ label: 'Agent Name', value: '{agent_name}' },
|
|
203
|
+
{ label: 'Ticket Number', value: '{ticket_id}' },
|
|
204
|
+
{ label: 'Customer Name', value: '{customer_name}' }
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
const handleSend = () => {
|
|
208
|
+
// Send message
|
|
209
|
+
setMessage('');
|
|
210
|
+
messageRef.current?.focus();
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<div className="chat-composer">
|
|
215
|
+
<VariableField
|
|
216
|
+
ref={messageRef}
|
|
217
|
+
value={message}
|
|
218
|
+
onChange={setMessage}
|
|
219
|
+
options={quickResponses}
|
|
220
|
+
placeholder="Type your message... @ for quick responses"
|
|
221
|
+
inputType="textarea"
|
|
222
|
+
/>
|
|
223
|
+
<button onClick={handleSend}>Send</button>
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Custom Variable Categories
|
|
230
|
+
```jsx
|
|
231
|
+
function CategorizedVariables() {
|
|
232
|
+
const [content, setContent] = useState('');
|
|
233
|
+
|
|
234
|
+
const variables = [
|
|
235
|
+
{ label: 'User: Name', value: '{user.name}', category: 'user' },
|
|
236
|
+
{ label: 'User: Email', value: '{user.email}', category: 'user' },
|
|
237
|
+
{ label: 'Order: ID', value: '{order.id}', category: 'order' },
|
|
238
|
+
{ label: 'Order: Total', value: '{order.total}', category: 'order' },
|
|
239
|
+
{ label: 'Date: Today', value: '{date.today}', category: 'date' },
|
|
240
|
+
{ label: 'Date: Time', value: '{date.time}', category: 'date' }
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<VariableField
|
|
245
|
+
value={content}
|
|
246
|
+
onChange={setContent}
|
|
247
|
+
options={variables}
|
|
248
|
+
label="Content with Variables"
|
|
249
|
+
help="Variables are organized by category for easier selection"
|
|
250
|
+
/>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Styling
|
|
256
|
+
|
|
257
|
+
The component uses these CSS classes:
|
|
258
|
+
|
|
259
|
+
```css
|
|
260
|
+
.variable-field-wrapper /* Main wrapper */
|
|
261
|
+
.variable-field-input /* Input element */
|
|
262
|
+
.variable-field-textarea /* Textarea element */
|
|
263
|
+
.variable-field-suggestions /* Suggestions dropdown */
|
|
264
|
+
.variable-field-suggestion /* Individual suggestion */
|
|
265
|
+
.variable-field-suggestion--highlighted /* Highlighted suggestion */
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Example custom styling:
|
|
269
|
+
```css
|
|
270
|
+
.variable-field-suggestions {
|
|
271
|
+
max-height: 200px;
|
|
272
|
+
overflow-y: auto;
|
|
273
|
+
border: 1px solid #ddd;
|
|
274
|
+
border-radius: 4px;
|
|
275
|
+
background: white;
|
|
276
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.variable-field-suggestion--highlighted {
|
|
280
|
+
background-color: #f0f0f0;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Accessibility
|
|
285
|
+
|
|
286
|
+
- Proper ARIA attributes for autocomplete
|
|
287
|
+
- Keyboard navigation fully supported
|
|
288
|
+
- Screen reader announcements for suggestions
|
|
289
|
+
- Focus management for better UX
|
|
290
|
+
- Descriptive labels and help text support
|
|
291
|
+
|
|
292
|
+
## Notes
|
|
293
|
+
|
|
294
|
+
- Variables can contain any string format (brackets, braces, etc.)
|
|
295
|
+
- Suggestions appear below the input by default
|
|
296
|
+
- The component handles text selection and cursor position
|
|
297
|
+
- Works with controlled and uncontrolled components
|
|
298
|
+
- Performance optimized with debounced filtering
|
|
299
|
+
- Ref forwarding allows parent components to control focus
|