@referralgps/selectra 1.0.0
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 +203 -0
- package/README.md +346 -0
- package/dist/selectra.css +1 -0
- package/dist/selectra.es.js +1318 -0
- package/dist/selectra.es.js.map +1 -0
- package/dist/selectra.iife.js +32 -0
- package/dist/selectra.iife.js.map +1 -0
- package/dist/selectra.umd.js +32 -0
- package/dist/selectra.umd.js.map +1 -0
- package/package.json +64 -0
- package/src-alpine/README.md +346 -0
- package/src-alpine/index.d.ts +217 -0
- package/src-alpine/index.js +70 -0
- package/src-alpine/plugins/index.js +219 -0
- package/src-alpine/selectize.js +1022 -0
- package/src-alpine/sifter.js +270 -0
- package/src-alpine/styles/selectra.css +277 -0
- package/src-alpine/utils.js +181 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for Selectize Alpine.js
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Escape HTML entities to prevent XSS
|
|
7
|
+
*/
|
|
8
|
+
export function escapeHtml(str) {
|
|
9
|
+
if (typeof str !== 'string') return '';
|
|
10
|
+
return str
|
|
11
|
+
.replace(/&/g, '&')
|
|
12
|
+
.replace(/</g, '<')
|
|
13
|
+
.replace(/>/g, '>')
|
|
14
|
+
.replace(/"/g, '"');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Debounce a function call
|
|
19
|
+
*/
|
|
20
|
+
export function debounce(fn, delay) {
|
|
21
|
+
let timer;
|
|
22
|
+
return function (...args) {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
timer = setTimeout(() => fn.apply(this, args), delay);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate a unique ID
|
|
30
|
+
*/
|
|
31
|
+
let idCounter = 0;
|
|
32
|
+
export function uid(prefix = 'selectize') {
|
|
33
|
+
return `${prefix}-${++idCounter}-${Math.random().toString(36).slice(2, 8)}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Hash a value to a string key
|
|
38
|
+
*/
|
|
39
|
+
export function hashKey(value) {
|
|
40
|
+
if (typeof value === 'undefined' || value === null) return null;
|
|
41
|
+
return typeof value === 'boolean'
|
|
42
|
+
? value ? '1' : '0'
|
|
43
|
+
: String(value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Highlight matching text in a string
|
|
48
|
+
*/
|
|
49
|
+
export function highlight(text, search) {
|
|
50
|
+
if (!search || !search.length) return escapeHtml(text);
|
|
51
|
+
const escaped = escapeHtml(text);
|
|
52
|
+
const regex = new RegExp(
|
|
53
|
+
'(' + search.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1') + ')',
|
|
54
|
+
'ig'
|
|
55
|
+
);
|
|
56
|
+
return escaped.replace(regex, '<span class="font-semibold text-inherit">$1</span>');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Measure the width of a string in pixels
|
|
61
|
+
*/
|
|
62
|
+
let measureCanvas;
|
|
63
|
+
export function measureString(str, el) {
|
|
64
|
+
if (!measureCanvas) {
|
|
65
|
+
measureCanvas = document.createElement('canvas');
|
|
66
|
+
}
|
|
67
|
+
const ctx = measureCanvas.getContext('2d');
|
|
68
|
+
if (el) {
|
|
69
|
+
const style = window.getComputedStyle(el);
|
|
70
|
+
ctx.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
|
|
71
|
+
}
|
|
72
|
+
return ctx.measureText(str).width;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Auto-grow an input element to fit its content
|
|
77
|
+
*/
|
|
78
|
+
export function autoGrow(input, extraWidth = 10) {
|
|
79
|
+
const val = input.value || input.placeholder || '';
|
|
80
|
+
const width = measureString(val, input) + extraWidth;
|
|
81
|
+
input.style.width = Math.max(width, 60) + 'px';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Deep merge objects (simple version)
|
|
86
|
+
*/
|
|
87
|
+
export function deepMerge(target, ...sources) {
|
|
88
|
+
for (const source of sources) {
|
|
89
|
+
if (!source) continue;
|
|
90
|
+
for (const key of Object.keys(source)) {
|
|
91
|
+
if (
|
|
92
|
+
source[key] &&
|
|
93
|
+
typeof source[key] === 'object' &&
|
|
94
|
+
!Array.isArray(source[key])
|
|
95
|
+
) {
|
|
96
|
+
target[key] = deepMerge(target[key] || {}, source[key]);
|
|
97
|
+
} else {
|
|
98
|
+
target[key] = source[key];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return target;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Determine if the given element is a <select> tag
|
|
107
|
+
*/
|
|
108
|
+
export function isSelectElement(el) {
|
|
109
|
+
return el.tagName && el.tagName.toLowerCase() === 'select';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Determine if the given element is an <input> tag
|
|
114
|
+
*/
|
|
115
|
+
export function isInputElement(el) {
|
|
116
|
+
return el.tagName && el.tagName.toLowerCase() === 'input';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Read options from a native <select> element
|
|
121
|
+
*/
|
|
122
|
+
export function readSelectOptions(selectEl) {
|
|
123
|
+
const options = [];
|
|
124
|
+
const optgroups = [];
|
|
125
|
+
const selectedValues = [];
|
|
126
|
+
|
|
127
|
+
const processOption = (optionEl, optgroup = null) => {
|
|
128
|
+
const value = optionEl.value;
|
|
129
|
+
const text = optionEl.textContent.trim();
|
|
130
|
+
const disabled = optionEl.disabled;
|
|
131
|
+
|
|
132
|
+
if (!value && !text) return;
|
|
133
|
+
|
|
134
|
+
const data = { value, text, disabled };
|
|
135
|
+
|
|
136
|
+
// Read data-* attributes
|
|
137
|
+
if (optionEl.dataset.data) {
|
|
138
|
+
try {
|
|
139
|
+
Object.assign(data, JSON.parse(optionEl.dataset.data));
|
|
140
|
+
} catch (_) {
|
|
141
|
+
// ignore
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (optgroup !== null) {
|
|
146
|
+
data.optgroup = optgroup;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (optionEl.selected) {
|
|
150
|
+
selectedValues.push(value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
options.push(data);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
for (const child of selectEl.children) {
|
|
157
|
+
if (child.tagName.toLowerCase() === 'optgroup') {
|
|
158
|
+
const groupId = child.getAttribute('label') || '';
|
|
159
|
+
optgroups.push({
|
|
160
|
+
value: groupId,
|
|
161
|
+
label: groupId,
|
|
162
|
+
disabled: child.disabled,
|
|
163
|
+
});
|
|
164
|
+
for (const opt of child.children) {
|
|
165
|
+
processOption(opt, groupId);
|
|
166
|
+
}
|
|
167
|
+
} else if (child.tagName.toLowerCase() === 'option') {
|
|
168
|
+
processOption(child);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { options, optgroups, selectedValues };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if element is RTL
|
|
177
|
+
*/
|
|
178
|
+
export function isRtl(el) {
|
|
179
|
+
const style = window.getComputedStyle(el);
|
|
180
|
+
return style.direction === 'rtl';
|
|
181
|
+
}
|