@siratik/wp-component-library 0.1.1 → 0.1.3
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 +186 -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,30 @@
|
|
|
1
|
+
# SegmentedBar Component
|
|
2
|
+
|
|
3
|
+
A row of equal segments, one per item, each in a status color. Use it for "3 of 4 on" summaries where each part matters, not just the total.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```jsx
|
|
8
|
+
import { SegmentedBar } from '@siratik/wp-component-library';
|
|
9
|
+
|
|
10
|
+
<SegmentedBar
|
|
11
|
+
label="Firewall: 3 of 4 on"
|
|
12
|
+
segments={ [
|
|
13
|
+
{ key: 'waf', intent: 'success' },
|
|
14
|
+
{ key: 'exploits', intent: 'success' },
|
|
15
|
+
{ key: 'xmlrpc', intent: 'warning' },
|
|
16
|
+
{ key: 'bots', intent: 'success' },
|
|
17
|
+
] }
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
| Prop | Type | Default | Description |
|
|
24
|
+
|------|------|---------|-------------|
|
|
25
|
+
| `segments` | `Array<{key: string, intent: string}>` | - | One segment each, in order. `intent` is `'success'`, `'warning'`, `'error'`, `'info'` or `'default'` (neutral gray) |
|
|
26
|
+
| `label` | `string` | - | Accessible summary. The bar is announced as an image with this label |
|
|
27
|
+
| `height` | `number` | `6` | Segment height in px |
|
|
28
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
29
|
+
|
|
30
|
+
Colors come from the shared status palette (`statusColor`), so they match `Badge` and the score charts.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SegmentedBar Component
|
|
3
|
+
*
|
|
4
|
+
* A row of equal segments, one per item, each in a status color — for
|
|
5
|
+
* "3 of 4 on" style summaries where every part matters, not just the total.
|
|
6
|
+
*
|
|
7
|
+
* @package WPComponentLibrary
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import styled from "@emotion/styled";
|
|
11
|
+
import { statusColor } from "../../constants/statusColors";
|
|
12
|
+
|
|
13
|
+
const Track = styled.div`
|
|
14
|
+
display: flex;
|
|
15
|
+
gap: 6px;
|
|
16
|
+
width: 100%;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const Segment = styled.span`
|
|
20
|
+
flex: 1;
|
|
21
|
+
height: ${({ $height }) => $height}px;
|
|
22
|
+
border-radius: ${({ $height }) => $height / 2}px;
|
|
23
|
+
background: ${({ $color }) => $color};
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const NEUTRAL = "var(--wpds-color-background-surface-neutral-weak, #f0f0f0)";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Color for a segment intent.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} intent 'success', 'warning', 'error', 'info' or 'default'.
|
|
32
|
+
* @return {string} CSS color.
|
|
33
|
+
*/
|
|
34
|
+
function segmentColor(intent) {
|
|
35
|
+
return ["success", "warning", "error", "info"].includes(intent)
|
|
36
|
+
? statusColor(intent, "fill")
|
|
37
|
+
: NEUTRAL;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* SegmentedBar Component
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} props
|
|
44
|
+
* @param {Array<{key: string, intent: string}>} props.segments One segment each, in order.
|
|
45
|
+
* @param {string} props.label Accessible summary, e.g. "Firewall: 3 of 4 on".
|
|
46
|
+
* @param {number} [props.height=6] Segment height in px.
|
|
47
|
+
* @param {string} [props.className] Additional CSS classes.
|
|
48
|
+
* @return {JSX.Element} Segmented bar.
|
|
49
|
+
*/
|
|
50
|
+
export function SegmentedBar({ segments, label, height = 6, className = "" }) {
|
|
51
|
+
return (
|
|
52
|
+
<Track
|
|
53
|
+
role="img"
|
|
54
|
+
aria-label={label}
|
|
55
|
+
className={`siratik-segmented-bar ${className}`.trim()}
|
|
56
|
+
>
|
|
57
|
+
{segments.map((segment) => (
|
|
58
|
+
<Segment
|
|
59
|
+
key={segment.key}
|
|
60
|
+
$height={height}
|
|
61
|
+
$color={segmentColor(segment.intent)}
|
|
62
|
+
/>
|
|
63
|
+
))}
|
|
64
|
+
</Track>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default SegmentedBar;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# SortableSelect Component
|
|
2
|
+
|
|
3
|
+
A flexible component that combines react-select with drag-and-drop sorting capabilities using dnd-kit. The selected tokens/pills within the react-select component itself are sortable via drag and drop. Perfect for creating searchable, sortable lists of posts, terms, users, or any custom data.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This component's dependencies are included with the component library. No additional installation is required.
|
|
8
|
+
|
|
9
|
+
### Dependencies Included:
|
|
10
|
+
- react-select (for the select functionality)
|
|
11
|
+
- @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities, @dnd-kit/modifiers (for drag and drop)
|
|
12
|
+
|
|
13
|
+
**Note about @emotion warnings:** You may see console warnings about multiple instances of @emotion/react. These warnings are harmless and occur because react-select bundles @emotion. The component will function correctly despite these warnings.
|
|
14
|
+
|
|
15
|
+
## Basic Usage
|
|
16
|
+
|
|
17
|
+
```jsx
|
|
18
|
+
import { SortableSelect } from '@siratik/wp-component-library';
|
|
19
|
+
import { useState } from '@wordpress/element';
|
|
20
|
+
|
|
21
|
+
function MyComponent() {
|
|
22
|
+
const [selected, setSelected] = useState([]);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<SortableSelect
|
|
26
|
+
value={selected}
|
|
27
|
+
onChange={setSelected}
|
|
28
|
+
options={[
|
|
29
|
+
{ value: '1', label: 'Option 1' },
|
|
30
|
+
{ value: '2', label: 'Option 2' },
|
|
31
|
+
{ value: '3', label: 'Option 3' },
|
|
32
|
+
]}
|
|
33
|
+
placeholder="Select options..."
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Props
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
|------|------|---------|-------------|
|
|
43
|
+
| `value` | `Array` | `[]` | Currently selected items |
|
|
44
|
+
| `onChange` | `Function` | required | Callback when selection or order changes |
|
|
45
|
+
| `options` | `Array` | - | Static options for sync mode |
|
|
46
|
+
| `loadOptions` | `Function` | - | Async function to load options |
|
|
47
|
+
| `getOptionLabel` | `Function` | `(option) => option.label \|\| option.name \|\| option.title` | Extract display label |
|
|
48
|
+
| `getOptionValue` | `Function` | `(option) => option.value \|\| option.id` | Extract unique value |
|
|
49
|
+
| `renderOption` | `Function` | - | Custom option renderer |
|
|
50
|
+
| `placeholder` | `string` | `"Search and select..."` | Placeholder text |
|
|
51
|
+
| `isMulti` | `boolean` | `true` | Enable multi-select |
|
|
52
|
+
| `isClearable` | `boolean` | `true` | Show clear button |
|
|
53
|
+
| `isSearchable` | `boolean` | `true` | Enable search |
|
|
54
|
+
| `isDisabled` | `boolean` | `false` | Disable component |
|
|
55
|
+
| `className` | `string` | `""` | Additional CSS classes |
|
|
56
|
+
| `maxItems` | `number` | - | Maximum selectable items |
|
|
57
|
+
| `filterOption` | `Function` | - | Custom filter function |
|
|
58
|
+
|
|
59
|
+
## Advanced Examples
|
|
60
|
+
|
|
61
|
+
### Async Post Search
|
|
62
|
+
|
|
63
|
+
```jsx
|
|
64
|
+
import { SortableSelect } from '@siratik/wp-component-library';
|
|
65
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
66
|
+
|
|
67
|
+
function PostSelector() {
|
|
68
|
+
const [selectedPosts, setSelectedPosts] = useState([]);
|
|
69
|
+
|
|
70
|
+
const searchPosts = async (inputValue) => {
|
|
71
|
+
const response = await apiFetch({
|
|
72
|
+
path: `/wp/v2/posts?search=${inputValue}&per_page=20`,
|
|
73
|
+
});
|
|
74
|
+
return response;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<SortableSelect
|
|
79
|
+
value={selectedPosts}
|
|
80
|
+
onChange={setSelectedPosts}
|
|
81
|
+
loadOptions={searchPosts}
|
|
82
|
+
getOptionLabel={(post) => post.title.rendered}
|
|
83
|
+
getOptionValue={(post) => post.id}
|
|
84
|
+
placeholder="Search posts..."
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Custom Rendering
|
|
91
|
+
|
|
92
|
+
```jsx
|
|
93
|
+
<SortableSelect
|
|
94
|
+
value={selectedUsers}
|
|
95
|
+
onChange={setSelectedUsers}
|
|
96
|
+
loadOptions={searchUsers}
|
|
97
|
+
renderOption={(user) => (
|
|
98
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
99
|
+
<img
|
|
100
|
+
src={user.avatar_urls['48']}
|
|
101
|
+
alt={user.name}
|
|
102
|
+
style={{ width: '24px', height: '24px', borderRadius: '50%' }}
|
|
103
|
+
/>
|
|
104
|
+
<div>
|
|
105
|
+
<div>{user.name}</div>
|
|
106
|
+
<small style={{ color: '#666' }}>{user.email}</small>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Variable Tags with Categories
|
|
114
|
+
|
|
115
|
+
```jsx
|
|
116
|
+
const variableTags = [
|
|
117
|
+
{ value: '{{site_name}}', label: 'Site Name', category: 'Site' },
|
|
118
|
+
{ value: '{{post_title}}', label: 'Post Title', category: 'Post' },
|
|
119
|
+
{ value: '{{author_name}}', label: 'Author Name', category: 'Author' },
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
<SortableSelect
|
|
123
|
+
value={selectedTags}
|
|
124
|
+
onChange={setSelectedTags}
|
|
125
|
+
options={variableTags}
|
|
126
|
+
renderOption={(option) => (
|
|
127
|
+
<div>
|
|
128
|
+
<strong>{option.label}</strong>
|
|
129
|
+
<code style={{ marginLeft: '8px' }}>{option.value}</code>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
/>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Styling
|
|
136
|
+
|
|
137
|
+
The component includes default styles and supports dark mode. You can customize the appearance using the provided CSS classes:
|
|
138
|
+
|
|
139
|
+
- `.sortable-select` - Main container
|
|
140
|
+
- `.sortable-select__control` - React-select control
|
|
141
|
+
- `.sortable-select__multi-value` - Individual token/pill
|
|
142
|
+
- `.sortable-select__drag-handle` - Drag handle icon within tokens
|
|
143
|
+
- `.sortable-select__multi-value__label` - Token label text
|
|
144
|
+
- `.sortable-select__multi-value__remove` - Token remove button
|
|
145
|
+
|
|
146
|
+
## Accessibility
|
|
147
|
+
|
|
148
|
+
The component includes proper ARIA labels and keyboard support:
|
|
149
|
+
- Arrow keys to navigate options
|
|
150
|
+
- Enter to select
|
|
151
|
+
- Tab to move between elements
|
|
152
|
+
- Drag tokens to reorder them within the select component
|
|
153
|
+
- The drag handle appears on hover for better UX
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { Button } from "@wordpress/components";
|
|
5
|
+
import { closeSmall } from "@wordpress/icons";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* External dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { useSortable } from "@dnd-kit/sortable";
|
|
11
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
12
|
+
import styled from '@emotion/styled';
|
|
13
|
+
|
|
14
|
+
// Styled components
|
|
15
|
+
const StyledToken = styled.span`
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 2px;
|
|
19
|
+
padding: 0 2px 0 6px;
|
|
20
|
+
height: 24px;
|
|
21
|
+
background: #ddd;
|
|
22
|
+
border-radius: 2px;
|
|
23
|
+
font-size: 13px;
|
|
24
|
+
cursor: grab;
|
|
25
|
+
position: relative;
|
|
26
|
+
|
|
27
|
+
&.is-dragging {
|
|
28
|
+
opacity: 0.5;
|
|
29
|
+
cursor: grabbing;
|
|
30
|
+
z-index: 1000;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.is-plain-text {
|
|
34
|
+
background: transparent;
|
|
35
|
+
border-radius: 0;
|
|
36
|
+
padding: 0;
|
|
37
|
+
cursor: move;
|
|
38
|
+
|
|
39
|
+
.sortable-select__token-text {
|
|
40
|
+
font-weight: normal;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&.is-variable {
|
|
45
|
+
background: #e0e0e0;
|
|
46
|
+
border: 1px solid #ccc;
|
|
47
|
+
font-family: monospace;
|
|
48
|
+
font-size: 12px;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const StyledTokenText = styled.span`
|
|
53
|
+
max-width: 200px;
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
text-overflow: ellipsis;
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const StyledTokenRemove = styled(Button)`
|
|
60
|
+
padding: 0;
|
|
61
|
+
height: 18px;
|
|
62
|
+
width: 18px;
|
|
63
|
+
min-width: 18px;
|
|
64
|
+
background: transparent;
|
|
65
|
+
border: none;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
|
|
68
|
+
// Override WordPress button styles
|
|
69
|
+
&.components-button.has-icon {
|
|
70
|
+
padding: 0;
|
|
71
|
+
width: 24px;
|
|
72
|
+
min-width: 24px;
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* CustomSortableToken Component
|
|
78
|
+
*
|
|
79
|
+
* A draggable token for the custom sortable select implementation.
|
|
80
|
+
*/
|
|
81
|
+
export default function CustomSortableToken({
|
|
82
|
+
id,
|
|
83
|
+
value,
|
|
84
|
+
onRemove,
|
|
85
|
+
disabled = false,
|
|
86
|
+
isPlainText = false,
|
|
87
|
+
isVariable = false,
|
|
88
|
+
}) {
|
|
89
|
+
const {
|
|
90
|
+
attributes,
|
|
91
|
+
listeners,
|
|
92
|
+
setNodeRef,
|
|
93
|
+
transform,
|
|
94
|
+
transition,
|
|
95
|
+
isDragging,
|
|
96
|
+
} = useSortable({
|
|
97
|
+
id,
|
|
98
|
+
disabled,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const style = {
|
|
102
|
+
transform: CSS.Translate.toString(transform),
|
|
103
|
+
transition,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<StyledToken
|
|
108
|
+
ref={setNodeRef}
|
|
109
|
+
style={style}
|
|
110
|
+
className={`${isDragging ? "is-dragging" : ""} ${isPlainText ? "is-plain-text" : ""} ${isVariable ? "is-variable" : ""}`}
|
|
111
|
+
{...attributes}
|
|
112
|
+
{...listeners}
|
|
113
|
+
>
|
|
114
|
+
<StyledTokenText>{value}</StyledTokenText>
|
|
115
|
+
<StyledTokenRemove
|
|
116
|
+
icon={closeSmall}
|
|
117
|
+
label="Remove"
|
|
118
|
+
onClick={(e) => {
|
|
119
|
+
e.stopPropagation();
|
|
120
|
+
onRemove();
|
|
121
|
+
}}
|
|
122
|
+
disabled={disabled}
|
|
123
|
+
size="small"
|
|
124
|
+
/>
|
|
125
|
+
</StyledToken>
|
|
126
|
+
);
|
|
127
|
+
}
|