@uniflowed/ui 0.0.0-alpha.9 → 0.2.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/accordion.js +84 -57
- package/alert-dialog.js +284 -0
- package/alert.js +142 -0
- package/avatar.js +280 -0
- package/breadcrumb.js +138 -0
- package/calendar.js +587 -0
- package/carousel.js +410 -0
- package/checkbox.js +215 -31
- package/collapsible.js +72 -48
- package/color-picker.js +172 -0
- package/combobox.js +216 -39
- package/context-menu.js +215 -0
- package/date-field.js +9 -0
- package/date-picker.js +357 -0
- package/date-range-picker.js +120 -0
- package/dialog.js +243 -178
- package/drag-drop.js +125 -0
- package/drawer.js +504 -0
- package/field.js +260 -43
- package/grid-list.js +8 -0
- package/hover-card.js +52 -52
- package/i18n-provider.js +89 -0
- package/index.js +1177 -31
- package/input-otp.js +218 -0
- package/interactions.js +2327 -0
- package/internal/anchor.js +71 -6
- package/internal/collection.js +562 -0
- package/internal/date-grid.js +260 -0
- package/internal/date-range.js +26 -0
- package/internal/disclosure.js +201 -0
- package/internal/menu-tree.js +228 -0
- package/internal/merge-props.js +85 -1
- package/internal/roving-focus.js +15 -4
- package/internal/segmented-field.js +317 -0
- package/internal/selection.js +171 -0
- package/internal/visually-hidden-style.js +41 -0
- package/list-box.js +13 -0
- package/menu.js +553 -361
- package/menubar.js +295 -0
- package/number-field.js +263 -0
- package/package.json +8 -28
- package/pagination.js +34 -22
- package/popover.js +116 -75
- package/progress.js +21 -16
- package/radio-group.js +81 -75
- package/range-calendar.js +79 -0
- package/resizable.js +155 -9
- package/scroll-area.js +283 -0
- package/select.js +83 -37
- package/separator.js +97 -0
- package/sheet.js +189 -0
- package/sidebar.js +320 -0
- package/skeleton.js +163 -0
- package/slider.js +95 -89
- package/switch.js +42 -34
- package/table.js +100 -71
- package/tabs.js +100 -91
- package/tag-group.js +8 -0
- package/time-field.js +8 -0
- package/toast.js +36 -66
- package/toggle-group.js +53 -49
- package/toggle.js +41 -27
- package/tooltip.js +48 -55
- package/tree.js +8 -0
- package/visually-hidden.js +259 -0
package/i18n-provider.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
import * as React from "@uniflowed/react";
|
|
5
|
+
import { createContext, useContext, useMemo } from "@uniflowed/react";
|
|
6
|
+
import type { RenderProp, Rest } from "./internal/merge-props.js";
|
|
7
|
+
import { withProps } from "./internal/merge-props.js";
|
|
8
|
+
|
|
9
|
+
export type Locale = {| readonly locale: string, readonly direction: "ltr" | "rtl" |};
|
|
10
|
+
const LocaleContext: React.Context<Locale> = createContext({ locale: "en-US", direction: "ltr" });
|
|
11
|
+
|
|
12
|
+
/** An explicit locale keeps server and client output identical. Nested providers form islands. */
|
|
13
|
+
export component I18nProvider(
|
|
14
|
+
children: React.Node,
|
|
15
|
+
locale?: string,
|
|
16
|
+
direction?: "ltr" | "rtl",
|
|
17
|
+
render?: RenderProp,
|
|
18
|
+
...rest: Rest
|
|
19
|
+
) {
|
|
20
|
+
const parent = useLocale();
|
|
21
|
+
const state = useMemo((): Locale => {
|
|
22
|
+
const resolved = new Intl.Locale(locale ?? parent.locale);
|
|
23
|
+
const script = resolved.maximize().script;
|
|
24
|
+
const rtl = ["Arab", "Hebr", "Thaa", "Nkoo", "Adlm", "Rohg"].includes(script ?? "");
|
|
25
|
+
return {
|
|
26
|
+
locale: resolved.toString(),
|
|
27
|
+
direction: direction ?? (locale == null ? parent.direction : rtl ? "rtl" : "ltr"),
|
|
28
|
+
};
|
|
29
|
+
}, [locale, direction, parent]);
|
|
30
|
+
const props = withProps(rest, { children, lang: state.locale, dir: state.direction });
|
|
31
|
+
return (
|
|
32
|
+
<LocaleContext.Provider value={state}>
|
|
33
|
+
{render != null ? render(props) : <div {...props} />}
|
|
34
|
+
</LocaleContext.Provider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export hook useLocale(): Locale {
|
|
39
|
+
return useContext(LocaleContext);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Share Intl's language-specific ordering with caller-owned collections. */
|
|
43
|
+
export hook useCollator(options?: Intl$CollatorOptions): Intl$Collator {
|
|
44
|
+
const { locale } = useLocale();
|
|
45
|
+
return useMemo(() => new Intl.Collator(locale, options), [locale, options]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Bound the cache when a long-lived server serves many requested locales.
|
|
49
|
+
const searchCollators = new Map<string, Intl$Collator>();
|
|
50
|
+
function searchCollator(locale: string): Intl$Collator {
|
|
51
|
+
const cached = searchCollators.get(locale);
|
|
52
|
+
if (cached != null) return cached;
|
|
53
|
+
const collator = new Intl.Collator(locale, { usage: "search", sensitivity: "base" });
|
|
54
|
+
if (searchCollators.size >= 32) searchCollators.clear();
|
|
55
|
+
searchCollators.set(locale, collator);
|
|
56
|
+
return collator;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function startsWithLocale(text: string, query: string, locale: string): boolean {
|
|
60
|
+
const value = text.normalize("NFC");
|
|
61
|
+
const needle = query.normalize("NFC");
|
|
62
|
+
return (
|
|
63
|
+
searchCollator(locale).compare(
|
|
64
|
+
Array.from(value).slice(0, Array.from(needle).length).join(""),
|
|
65
|
+
needle,
|
|
66
|
+
) === 0
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Filtering uses the same collation as typeahead; consumers own the result list. */
|
|
71
|
+
export hook useFilter(): {
|
|
72
|
+
startsWith: (text: string, query: string) => boolean,
|
|
73
|
+
contains: (text: string, query: string) => boolean,
|
|
74
|
+
} {
|
|
75
|
+
const { locale } = useLocale();
|
|
76
|
+
return useMemo(
|
|
77
|
+
() => ({
|
|
78
|
+
startsWith: (text, query) => startsWithLocale(text, query, locale),
|
|
79
|
+
contains: (text, query) => {
|
|
80
|
+
const chars = Array.from(text.normalize("NFC"));
|
|
81
|
+
return (
|
|
82
|
+
query === "" ||
|
|
83
|
+
chars.some((_, index) => startsWithLocale(chars.slice(index).join(""), query, locale))
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
[locale],
|
|
88
|
+
);
|
|
89
|
+
}
|