@goplusvn/core 0.1.96 → 0.1.98
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## 0.1.98 — Chuẩn hóa Modal FocusScope cho Combobox/MultiSelect khi nằm trong Dialog/Sheet/Modal
|
|
2
|
+
|
|
3
|
+
Khi Combobox hoặc MultiSelect nằm trong Modal/Dialog/Sheet (như Modal thanh toán POS hay Sheet bộ lọc nâng cao),
|
|
4
|
+
cơ chế FocusScope Stack của Radix yêu cầu `modal={true}` kết hợp với auto-focus vào `searchInputRef`
|
|
5
|
+
và loại bỏ hoàn toàn các rào cản `stopPropagation` trên `onFocus`/`onClick` của Input.
|
|
6
|
+
|
|
7
|
+
**Đổi**
|
|
8
|
+
|
|
9
|
+
- `ui/primitives/combobox.tsx` & `ui/forms/multi-select.tsx` — kích hoạt `modal={true}` mặc định,
|
|
10
|
+
tự động focus vào `searchInputRef` khi mở dropdown, bổ sung `onPointerDown` stopPropagation trên Input
|
|
11
|
+
để người dùng click/chạm vào ô tìm kiếm lập tức nhận con trỏ nhập liệu và bật bàn phím.
|
|
12
|
+
|
|
13
|
+
## 0.1.97 — Sửa triệt để tương tác chạm và nhập liệu cho ô tìm kiếm Combobox/MultiSelect
|
|
14
|
+
|
|
15
|
+
Khi `modal={true}` được bật mặc định, Radix Popover kích hoạt FocusScope + ScrollLock
|
|
16
|
+
khiến thiết bị cảm ứng (Mobile/Tablet) và các trình duyệt bị xung đột:
|
|
17
|
+
chạm vào ô tìm kiếm không mở được bàn phím ảo hoặc bị đóng dropdown / mất focus.
|
|
18
|
+
|
|
19
|
+
**Đổi**
|
|
20
|
+
|
|
21
|
+
- `ui/primitives/combobox.tsx` & `ui/forms/multi-select.tsx` — chuyển `modal` về mặc định `false`,
|
|
22
|
+
loại bỏ `e.stopPropagation()` trên `onFocus`/`onClick` của `Input`, thêm `onPointerDown={(e) => e.stopPropagation()}`
|
|
23
|
+
để input nhận focus tự nhiên và kích hoạt bàn phím ảo mượt mà trên mọi thiết bị.
|
|
24
|
+
- `ui/forms/multi-select.tsx` — sửa hàm `findMatchRange` xử lý chính xác ký tự khoảng trắng khi highlight.
|
|
25
|
+
|
|
1
26
|
## 0.1.96 — Sửa lỗi Focus Trap khiến ô tìm kiếm Combobox/MultiSelect mất focus khi lồng trong Sheet/Dialog
|
|
2
27
|
|
|
3
28
|
Khi `Combobox` hoặc `MultiSelect` nằm trong `<Sheet>` (bộ lọc mobile) hoặc `<Dialog>`,
|
package/package.json
CHANGED
|
@@ -148,6 +148,14 @@ const normalizeVietnamese = (str: string) =>
|
|
|
148
148
|
.toLowerCase()
|
|
149
149
|
: "";
|
|
150
150
|
|
|
151
|
+
const normalizeVietnameseChar = (char: string) => {
|
|
152
|
+
return char
|
|
153
|
+
.normalize("NFD")
|
|
154
|
+
.replace(/[\u0300-\u036f\u031b\u0323]/g, "")
|
|
155
|
+
.replace(/[đĐ]/g, (m) => (m === "đ" ? "d" : "D"))
|
|
156
|
+
.toLowerCase();
|
|
157
|
+
};
|
|
158
|
+
|
|
151
159
|
/**
|
|
152
160
|
* Tìm vị trí khớp (không dấu, không phân biệt hoa thường, hỗ trợ đ/Đ) của query trong
|
|
153
161
|
* label GỐC — trả về [start, end) trên chuỗi gốc để highlight.
|
|
@@ -158,7 +166,7 @@ function findMatchRange(label: string, query: string): [number, number] | null {
|
|
|
158
166
|
let stripped = "";
|
|
159
167
|
const indexMap: number[] = [];
|
|
160
168
|
for (let i = 0; i < label.length; i++) {
|
|
161
|
-
const s =
|
|
169
|
+
const s = normalizeVietnameseChar(label[i]);
|
|
162
170
|
for (let j = 0; j < s.length; j++) {
|
|
163
171
|
stripped += s[j];
|
|
164
172
|
indexMap.push(i);
|
|
@@ -726,10 +734,6 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
726
734
|
e.preventDefault();
|
|
727
735
|
searchInputRef.current?.focus();
|
|
728
736
|
}}
|
|
729
|
-
onFocusOutside={(e) => {
|
|
730
|
-
// Ngăn Dialog / Sheet cha giật focus khỏi ô tìm kiếm của Popover portal
|
|
731
|
-
e.preventDefault();
|
|
732
|
-
}}
|
|
733
737
|
className="z-[100] flex w-[var(--radix-popover-trigger-width)] min-w-[15rem] max-w-[calc(100vw-1rem)] flex-col overflow-hidden rounded-md border bg-popover/95 p-0 text-popover-foreground shadow-lg shadow-black/5 backdrop-blur-md"
|
|
734
738
|
style={{
|
|
735
739
|
maxHeight:
|
|
@@ -749,9 +753,11 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
749
753
|
placeholder={searchPlaceholder}
|
|
750
754
|
value={searchValue}
|
|
751
755
|
onChange={(e) => {
|
|
752
|
-
e.stopPropagation();
|
|
753
756
|
setSearchValue(e.target.value);
|
|
754
757
|
}}
|
|
758
|
+
onPointerDown={(e) => {
|
|
759
|
+
e.stopPropagation();
|
|
760
|
+
}}
|
|
755
761
|
onKeyDown={(e) => {
|
|
756
762
|
// Prevent Dialog from intercepting keyboard events
|
|
757
763
|
e.stopPropagation();
|
|
@@ -778,12 +784,6 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
778
784
|
updateValue(value.slice(0, -1));
|
|
779
785
|
}
|
|
780
786
|
}}
|
|
781
|
-
onClick={(e) => {
|
|
782
|
-
e.stopPropagation();
|
|
783
|
-
}}
|
|
784
|
-
onFocus={(e) => {
|
|
785
|
-
e.stopPropagation();
|
|
786
|
-
}}
|
|
787
787
|
className="border-0 focus-visible:ring-0 focus-visible:ring-offset-0 h-7 bg-transparent px-0 text-sm shadow-none"
|
|
788
788
|
/>
|
|
789
789
|
{searchValue && (
|
|
@@ -25,8 +25,8 @@ interface ComboboxProps {
|
|
|
25
25
|
className?: string;
|
|
26
26
|
id?: string;
|
|
27
27
|
/**
|
|
28
|
-
* Chế độ modal cho Popover (mặc định true). Bắt buộc
|
|
29
|
-
*
|
|
28
|
+
* Chế độ modal cho Popover (mặc định true). Bắt buộc true để kích hoạt
|
|
29
|
+
* FocusScope Stack của Radix khi nằm trong Dialog/Sheet/Modal.
|
|
30
30
|
*/
|
|
31
31
|
modal?: boolean;
|
|
32
32
|
/**
|
|
@@ -180,10 +180,6 @@ export function Combobox({
|
|
|
180
180
|
e.preventDefault();
|
|
181
181
|
searchInputRef.current?.focus();
|
|
182
182
|
}}
|
|
183
|
-
onFocusOutside={(e) => {
|
|
184
|
-
// Ngăn Dialog / Sheet cha giật focus khỏi ô tìm kiếm của Popover portal
|
|
185
|
-
e.preventDefault();
|
|
186
|
-
}}
|
|
187
183
|
className="z-[100] w-[var(--radix-popover-trigger-width)] min-w-[12rem] max-w-[calc(100vw-1rem)] p-0 flex flex-col overflow-hidden"
|
|
188
184
|
style={{
|
|
189
185
|
maxHeight: `min(${maxHeight}, var(--radix-popover-content-available-height, ${maxHeight}))`,
|
|
@@ -197,9 +193,11 @@ export function Combobox({
|
|
|
197
193
|
placeholder={searchPlaceholder}
|
|
198
194
|
value={searchValue}
|
|
199
195
|
onChange={(e) => {
|
|
200
|
-
e.stopPropagation();
|
|
201
196
|
setSearchValue(e.target.value);
|
|
202
197
|
}}
|
|
198
|
+
onPointerDown={(e) => {
|
|
199
|
+
e.stopPropagation();
|
|
200
|
+
}}
|
|
203
201
|
onKeyDown={(e) => {
|
|
204
202
|
// Prevent Dialog from intercepting keyboard events
|
|
205
203
|
e.stopPropagation();
|
|
@@ -216,10 +214,7 @@ export function Combobox({
|
|
|
216
214
|
}
|
|
217
215
|
}
|
|
218
216
|
}}
|
|
219
|
-
|
|
220
|
-
e.stopPropagation();
|
|
221
|
-
}}
|
|
222
|
-
className="border-0 focus-visible:ring-0 focus-visible:ring-offset-0 h-9 bg-transparent px-0"
|
|
217
|
+
className="border-0 focus-visible:ring-0 focus-visible:ring-offset-0 h-9 bg-transparent px-0 text-sm"
|
|
223
218
|
/>
|
|
224
219
|
{searchValue && (
|
|
225
220
|
<Button
|