@lotics/ui 11.7.1 → 11.7.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.
@@ -254,7 +254,8 @@ an empty overlay with no content to derive a name from.
254
254
 
255
255
  Make a register SELECTABLE with the `Table` `leading` gutter + `selectAll` slot — a
256
256
  `CheckboxInput` per `TableRow` (its `leading` slot) + a select-all in the header band, the ticked
257
- rows `marked`, paired with a `FloatingActionBar`. The selection state is the **`useSelection()`**
257
+ rows `marked`, paired with a `FloatingActionBar` (its "Clear" escape is locale-resolved — pass
258
+ `clearLabel` only to override it). The selection state is the **`useSelection()`**
258
259
  hook (`@lotics/ui/use_selection`: `selected` / `has` / `count` / `toggle` / `setAll` /
259
260
  `allSelected` / `indeterminate` / `clear`) — gating which rows CAN be ticked stays with you: pass
260
261
  only the selectable ids to `setAll`/`allSelected`/`indeterminate` (a non-selectable row gets a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "11.7.1",
3
+ "version": "11.7.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",
package/src/combobox.tsx CHANGED
@@ -511,7 +511,6 @@ export function ComboboxContent(props: ComboboxContentProps) {
511
511
  const label = opt.label ?? opt.value;
512
512
  const content = !isCustom ? renderOptionContent?.(opt) : undefined;
513
513
  const shared = {
514
- key: `${row.kind}-${opt.value}`,
515
514
  nativeID: row.nativeID,
516
515
  testID: isCustom ? "combobox-custom-option" : `combobox-option-${opt.value}`,
517
516
  role: "option" as const,
@@ -526,9 +525,10 @@ export function ComboboxContent(props: ComboboxContentProps) {
526
525
  // Rich row (custom content, or a label over its description) → the growing
527
526
  // `MenuListItem`; a plain option stays the fixed-height `MenuButton`.
528
527
  return content || desc ? (
529
- <MenuListItem {...shared} title={content ?? label} description={content ? undefined : desc} />
528
+ <MenuListItem key={`${row.kind}-${opt.value}`} {...shared} title={content ?? label} description={content ? undefined : desc} />
530
529
  ) : (
531
530
  <MenuButton
531
+ key={`${row.kind}-${opt.value}`}
532
532
  {...shared}
533
533
  title={
534
534
  <Text userSelect="none" numberOfLines={1} weight={isCustom ? "medium" : undefined}>
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from "react";
2
2
  import { StyleSheet, View } from "react-native";
3
3
  import { Button } from "./button";
4
+ import { useLoticsLocale } from "./locale";
4
5
  import { Card } from "./card";
5
6
  import { Text } from "./text";
6
7
 
@@ -11,6 +12,7 @@ export interface FloatingActionBarProps {
11
12
  label: string;
12
13
  /** Clears the selection — the always-present escape. */
13
14
  onClear: () => void;
15
+ /** Overrides the localized default ("Clear" / "Bỏ chọn"). */
14
16
  clearLabel?: string;
15
17
  /** The bulk action(s) — a primary `Button` or an "Assign to…" `Popover`. */
16
18
  children: ReactNode;
@@ -25,7 +27,8 @@ export interface FloatingActionBarProps {
25
27
  * `box-none` wrapper lets clicks fall through everywhere except the bar.
26
28
  */
27
29
  export function FloatingActionBar(props: FloatingActionBarProps) {
28
- const { count, label, onClear, clearLabel = "Clear", children } = props;
30
+ const loc = useLoticsLocale().floatingActionBar;
31
+ const { count, label, onClear, clearLabel = loc.clear, children } = props;
29
32
  if (count <= 0) return null;
30
33
  return (
31
34
  <View pointerEvents="box-none" style={styles.wrap}>
package/src/locale.tsx CHANGED
@@ -36,6 +36,9 @@ export interface LoticsLocale {
36
36
  /** `FilterChip` (and `ColumnFilter`): the generic clear affordance, used when
37
37
  * a call site doesn't pass a dimension-specific `clearLabel`. */
38
38
  filterChip: { clear: string };
39
+ /** `FloatingActionBar`: the escape that drops the selection. The COUNT's noun is
40
+ * the call site's `label` (it names the rows), so only the escape lives here. */
41
+ floatingActionBar: { clear: string };
39
42
  /** `FormField`: the "Optional" marker shown next to the label when `optional`. */
40
43
  formField: { optional: string };
41
44
  /** `Drawer`: the record prev/next + close controls (screen-reader names). */
@@ -107,6 +110,7 @@ export const en: LoticsLocale = {
107
110
  datePicker: { today: "Today", now: "Now", clear: "Clear", done: "Done", openCalendar: "Open calendar", time: "Time", startTime: "Start time", endTime: "End time", addTime: "Add time", removeTime: "Remove time", year: "Year", month: "Month", day: "Day", hour: "Hour", minute: "Minute", dayPeriod: "AM/PM" },
108
111
  calendar: { previousMonth: "Previous month", nextMonth: "Next month" },
109
112
  filterChip: { clear: "Clear" },
113
+ floatingActionBar: { clear: "Clear" },
110
114
  formField: { optional: "Optional" },
111
115
  drawer: { previous: "Previous record", next: "Next record", close: "Close" },
112
116
  inline: { saveError: "Couldn't save. Try again.", save: "Save", cancel: "Cancel" },
@@ -186,6 +190,7 @@ export const vi: LoticsLocale = {
186
190
  datePicker: { today: "Hôm nay", now: "Bây giờ", clear: "Xóa", done: "Xong", openCalendar: "Mở lịch", time: "Giờ", startTime: "Giờ bắt đầu", endTime: "Giờ kết thúc", addTime: "Thêm giờ", removeTime: "Bỏ giờ", year: "Năm", month: "Tháng", day: "Ngày", hour: "Giờ", minute: "Phút", dayPeriod: "SA/CH" },
187
191
  calendar: { previousMonth: "Tháng trước", nextMonth: "Tháng sau" },
188
192
  filterChip: { clear: "Xóa" },
193
+ floatingActionBar: { clear: "Bỏ chọn" },
189
194
  formField: { optional: "Tùy chọn" },
190
195
  drawer: { previous: "Bản ghi trước", next: "Bản ghi sau", close: "Đóng" },
191
196
  inline: { saveError: "Không lưu được. Thử lại.", save: "Lưu", cancel: "Hủy" },
@@ -122,7 +122,6 @@ export function OptionList<T extends string, MULTI extends boolean = false, D =
122
122
  const label = opt.label ?? opt.value;
123
123
  const content = row.kind === "option" ? renderOptionContent?.(opt) : undefined;
124
124
  const shared = {
125
- key: `${row.kind}-${opt.value}`,
126
125
  nativeID: row.nativeID,
127
126
  testID: isCustom ? "option-list-custom" : opt.testID || `picker-option-${opt.value}`,
128
127
  role: "option" as const,
@@ -146,9 +145,10 @@ export function OptionList<T extends string, MULTI extends boolean = false, D =
146
145
  // description — is `MenuListItem`: the listbox row that GROWS. `MenuButton` is
147
146
  // the fixed-height single-line menu row, and stays that for plain options.
148
147
  return content || desc ? (
149
- <MenuListItem {...shared} title={content ?? label} description={content ? undefined : desc} />
148
+ <MenuListItem key={`${row.kind}-${opt.value}`} {...shared} title={content ?? label} description={content ? undefined : desc} />
150
149
  ) : (
151
150
  <MenuButton
151
+ key={`${row.kind}-${opt.value}`}
152
152
  {...shared}
153
153
  title={
154
154
  <Text userSelect="none" numberOfLines={1} weight={isCustom ? "medium" : undefined}>