PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.1.5
Tableberg – Simple Gutenberg Table Block v1.1.5
1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 trunk 0.0.2 0.2.1 0.3.2 0.3.3 0.4.1 0.5.0 0.5.1 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 All 42 releases
tableberg / components / icon-library / IconPickerMini.tsx

IconPickerMini.tsx in Tableberg – Simple Gutenberg Table Block 1.1.5, at components/icon-library/IconPickerMini.tsx

226 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from "@wordpress/i18n";
2 import { useEffect, useRef, useState } from "react";
3
4 import { tablebergIcons } from "./icons";
5 import { Button, SearchControl } from "@wordpress/components";
6 import { isEmpty } from "lodash";
7 import type { IconsLibraryIconProps, IconSvgData } from "./";
8
9 interface Props {
10 onSelect: (icon: IconsLibraryIconProps) => void;
11 maxHeight?: string;
12 }
13
14 function extractSvgData(icon: JSX.Element): IconSvgData {
15 const props = icon.props as {
16 viewBox?: string;
17 children?: { props?: { d?: string } };
18 };
19 return {
20 viewBox: props.viewBox ?? "0 0 24 24",
21 path: props.children?.props?.d ?? "",
22 };
23 }
24
25 const ALL_ICONS: Array<any> = [];
26
27 tablebergIcons.forEach((pack: any) => {
28 pack.icons.forEach((icon: any) => {
29 ALL_ICONS.push(icon);
30 });
31 });
32
33 const stringDistance = (function () {
34 // Algorithm: https://github.com/gustf/js-levenshtein/blob/master/index.js
35 function _min(
36 d0: number,
37 d1: number,
38 d2: number,
39 bx: number,
40 ay: number
41 ): number {
42 return d0 < d1 || d2 < d1
43 ? d0 > d2
44 ? d2 + 1
45 : d0 + 1
46 : bx === ay
47 ? d1
48 : d1 + 1;
49 }
50
51 return function (a: string, b: string): number {
52 if (a === b) {
53 return 0;
54 }
55
56 if (a.length > b.length) {
57 let tmp = a;
58 a = b;
59 b = tmp;
60 }
61
62 let la = a.length;
63 let lb = b.length;
64
65 while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
66 la--;
67 lb--;
68 }
69
70 let offset = 0;
71
72 while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
73 offset++;
74 }
75
76 la -= offset;
77 lb -= offset;
78
79 if (la === 0 || lb < 3) {
80 return lb;
81 }
82
83 let x = 0;
84 let y: number;
85 let d0: number;
86 let d1: number;
87 let d2: number;
88 let d3: number;
89 let dd: number = 0;
90 let dy: number;
91 let ay: number;
92 let bx0: number;
93 let bx1: number;
94 let bx2: number;
95 let bx3: number;
96
97 const vector: number[] = [];
98
99 for (y = 0; y < la; y++) {
100 vector.push(y + 1);
101 vector.push(a.charCodeAt(offset + y));
102 }
103
104 let len = vector.length - 1;
105
106 for (; x < lb - 3; ) {
107 bx0 = b.charCodeAt(offset + (d0 = x));
108 bx1 = b.charCodeAt(offset + (d1 = x + 1));
109 bx2 = b.charCodeAt(offset + (d2 = x + 2));
110 bx3 = b.charCodeAt(offset + (d3 = x + 3));
111 dd = x += 4;
112 for (y = 0; y < len; y += 2) {
113 dy = vector[y];
114 ay = vector[y + 1];
115 d0 = _min(dy, d0, d1, bx0, ay);
116 d1 = _min(d0, d1, d2, bx1, ay);
117 d2 = _min(d1, d2, d3, bx2, ay);
118 dd = _min(d2, d3, dd, bx3, ay);
119 vector[y] = dd;
120 d3 = d2;
121 d2 = d1;
122 d1 = d0;
123 d0 = dy;
124 }
125 }
126
127 for (; x < lb; ) {
128 bx0 = b.charCodeAt(offset + (d0 = x));
129 dd = ++x;
130 for (y = 0; y < len; y += 2) {
131 dy = vector[y];
132 vector[y] = dd = _min(dy, d0, dd, bx0, vector[y + 1]);
133 d0 = dy;
134 }
135 }
136
137 return dd;
138 };
139 })();
140
141 export default function IconPickerMini({ onSelect, maxHeight }: Props) {
142 maxHeight ||= "250px";
143
144 const [search, setSearch] = useState("");
145 const [icons, setIcons] = useState([]);
146 const [debouncedSearch, setDebouncedSearch] = useState("");
147 const [iconsKey, setIconsKey] = useState(0);
148
149 const toutRef = useRef<any>();
150
151 useEffect(() => {
152 const s = debouncedSearch.trim();
153 if (s === "") {
154 setIcons(ALL_ICONS as any);
155 return;
156 }
157 const icons: any = [];
158 const weighted = ALL_ICONS.map(icon => ({
159 icon,
160 weigth: stringDistance(debouncedSearch, icon.name),
161 }));
162
163 weighted.sort((a, b) => a.weigth - b.weigth);
164
165 const len = Math.min(20, weighted.length);
166
167 for (let i = 0; i < len; i++) {
168 icons.push(weighted[i].icon);
169 }
170
171 setIcons(icons);
172 setIconsKey(i => i + 1);
173 }, [debouncedSearch]);
174
175 useEffect(() => {
176 if (toutRef.current) {
177 clearTimeout(toutRef.current);
178 }
179 toutRef.current = setTimeout(() => {
180 setDebouncedSearch(search);
181 toutRef.current = false;
182 }, 500);
183 return () => {
184 if (toutRef.current) {
185 clearTimeout(toutRef.current);
186 }
187 };
188 }, [search]);
189
190 const isNoResults = isEmpty(icons);
191
192 return (
193 <div className="tableberg_icons_library_mini">
194 <SearchControl
195 value={search}
196 onChange={newValue => {
197 setSearch(newValue);
198 }}
199 placeholder={__("Search Icon", "tableberg")}
200 />
201 <div
202 className="tableberg_icons_library_mini_icons"
203 key={iconsKey}
204 style={{ maxHeight }}
205 >
206 {icons.map((icon: any) => (
207 <Button
208 key={icon.name}
209 title={icon.name}
210 className={`tableberg_min_icon_library_item`}
211 onClick={() =>
212 onSelect({
213 iconName: icon.name,
214 svg: extractSvgData(icon.icon),
215 })
216 }
217 >
218 {icon.icon}
219 </Button>
220 ))}
221 {isNoResults && <p>{__("No icons found.", "tableberg")}</p>}
222 </div>
223 </div>
224 );
225 }
226