fluent-styles 1.55.0 → 1.56.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/README.md +1 -873
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -44,10 +44,6 @@ A comprehensive, TypeScript-first React Native UI library providing production-r
|
|
|
44
44
|
- [StyledProgressBar](#styledprogressbar)
|
|
45
45
|
- [StyledSlider](#styledslider)
|
|
46
46
|
- [StyledDatePicker](#styleddatepicker)
|
|
47
|
-
- [StyledBottomSheet](#styledbottomsheet)
|
|
48
|
-
- [StyledEmptyState](#styledemptystate)
|
|
49
|
-
- [StyledSearchBar](#styledsearchbar)
|
|
50
|
-
- [StyledSkeleton](#styledskeleton)
|
|
51
47
|
- [Hooks](#hooks)
|
|
52
48
|
- [useToast](#usetoast)
|
|
53
49
|
- [useNotification](#usenotification)
|
|
@@ -107,7 +103,7 @@ export default function App() {
|
|
|
107
103
|
return (
|
|
108
104
|
<GlobalPortalProvider>
|
|
109
105
|
<PortalManager>
|
|
110
|
-
<YourNavigator />
|
|
106
|
+
<YourNavigator />z
|
|
111
107
|
</PortalManager>
|
|
112
108
|
</GlobalPortalProvider>
|
|
113
109
|
)
|
|
@@ -2681,874 +2677,6 @@ const DURATION = 243
|
|
|
2681
2677
|
|
|
2682
2678
|
---
|
|
2683
2679
|
|
|
2684
|
-
### StyledDatePicker
|
|
2685
|
-
|
|
2686
|
-
Pure-JS date / time picker — no native dependencies. Built entirely on fluent-styles primitives. Supports 5 modes, 3 display variants, scroll-drum time selection, month grid, date range, `minDate` / `maxDate` guards, and full colour overrides.
|
|
2687
|
-
|
|
2688
|
-
```tsx
|
|
2689
|
-
import { StyledDatePicker } from 'fluent-styles'
|
|
2690
|
-
```
|
|
2691
|
-
|
|
2692
|
-
#### Modes
|
|
2693
|
-
|
|
2694
|
-
| Mode | Description |
|
|
2695
|
-
|---|---|
|
|
2696
|
-
| `date` | Calendar grid — pick a single day (default) |
|
|
2697
|
-
| `time` | Scroll-drum hour / minute / AM-PM picker |
|
|
2698
|
-
| `datetime` | Calendar grid + time drum combined |
|
|
2699
|
-
| `range` | Two-tap calendar — start then end date |
|
|
2700
|
-
| `month` | Month grid + year scroll drum |
|
|
2701
|
-
|
|
2702
|
-
#### Variants
|
|
2703
|
-
|
|
2704
|
-
| Variant | Description |
|
|
2705
|
-
|---|---|
|
|
2706
|
-
| `inline` | Always-visible calendar embedded in the layout (default) |
|
|
2707
|
-
| `sheet` | Picker slides up in a Modal bottom-sheet |
|
|
2708
|
-
| `input` | Tappable input field that opens the sheet |
|
|
2709
|
-
|
|
2710
|
-
#### Sizes
|
|
2711
|
-
|
|
2712
|
-
| Size | Day-cell size | Font |
|
|
2713
|
-
|---|---|---|
|
|
2714
|
-
| `sm` | 34 px | 13 px |
|
|
2715
|
-
| `md` | 40 px (default) | 15 px |
|
|
2716
|
-
| `lg` | 48 px | 17 px |
|
|
2717
|
-
|
|
2718
|
-
#### `StyledDatePickerColors`
|
|
2719
|
-
|
|
2720
|
-
| Field | Default | Description |
|
|
2721
|
-
|---|---|---|
|
|
2722
|
-
| `selected` | `gray[900]` | Selected day circle fill |
|
|
2723
|
-
| `selectedText` | `white` | Selected day text |
|
|
2724
|
-
| `today` | `#3b82f6` | Today ring / text colour |
|
|
2725
|
-
| `rangeFill` | `selected + 9% alpha` | Fill between range start/end |
|
|
2726
|
-
| `dayText` | `gray[800]` | Regular day text |
|
|
2727
|
-
| `disabledText` | `gray[300]` | Out-of-range day text |
|
|
2728
|
-
| `headerText` | `gray[900]` | Month/year header text |
|
|
2729
|
-
| `background` | `white` | Sheet / inline background |
|
|
2730
|
-
| `inputBorder` | `gray[200]` | Input field border |
|
|
2731
|
-
| `confirmBg` | `gray[900]` | Confirm button background |
|
|
2732
|
-
| `confirmText` | `white` | Confirm button text |
|
|
2733
|
-
|
|
2734
|
-
#### Props
|
|
2735
|
-
|
|
2736
|
-
| Prop | Type | Default | Description |
|
|
2737
|
-
|---|---|---|---|
|
|
2738
|
-
| `mode` | `DatePickerMode` | `'date'` | Calendar mode |
|
|
2739
|
-
| `variant` | `DatePickerVariant` | `'inline'` | Display variant |
|
|
2740
|
-
| `size` | `DatePickerSize` | `'md'` | Day-cell size preset |
|
|
2741
|
-
| `value` | `Date \| null` | — | Selected date (`date` / `time` / `datetime` / `month`) |
|
|
2742
|
-
| `valueStart` | `Date \| null` | — | Range start date (`range` mode) |
|
|
2743
|
-
| `valueEnd` | `Date \| null` | — | Range end date (`range` mode) |
|
|
2744
|
-
| `minDate` | `Date` | — | Earliest selectable date |
|
|
2745
|
-
| `maxDate` | `Date` | — | Latest selectable date |
|
|
2746
|
-
| `showTodayButton` | `boolean` | `true` | Show "Today" shortcut button |
|
|
2747
|
-
| `showConfirm` | `boolean` | `true` | Show Confirm button in sheet |
|
|
2748
|
-
| `confirmLabel` | `string` | `'Done'` | Confirm button label |
|
|
2749
|
-
| `placeholder` | `string` | — | Input variant placeholder text |
|
|
2750
|
-
| `label` | `string` | — | Input variant field label |
|
|
2751
|
-
| `formatDisplay` | `(date: Date) => string` | auto | Custom input display formatter |
|
|
2752
|
-
| `colors` | `StyledDatePickerColors` | dark theme | Colour overrides |
|
|
2753
|
-
| `onChange` | `(date: Date) => void` | — | Fires on every date selection |
|
|
2754
|
-
| `onRangeChange` | `(start, end: Date\|null) => void` | — | Fires when either range thumb changes |
|
|
2755
|
-
| `onConfirm` | `(date: Date\|null) => void` | — | Fires when Confirm is tapped |
|
|
2756
|
-
| `disabled` | `boolean` | `false` | Disables all interaction |
|
|
2757
|
-
|
|
2758
|
-
#### Usage
|
|
2759
|
-
|
|
2760
|
-
```tsx
|
|
2761
|
-
import React, { useState } from 'react'
|
|
2762
|
-
import { StyledDatePicker } from 'fluent-styles'
|
|
2763
|
-
|
|
2764
|
-
// ── 1. Date — always-visible inline calendar ──────────────────────────────────
|
|
2765
|
-
const [date, setDate] = useState<Date | null>(new Date())
|
|
2766
|
-
|
|
2767
|
-
<StyledDatePicker
|
|
2768
|
-
mode="date"
|
|
2769
|
-
variant="inline"
|
|
2770
|
-
value={date}
|
|
2771
|
-
onChange={setDate}
|
|
2772
|
-
showTodayButton
|
|
2773
|
-
/>
|
|
2774
|
-
|
|
2775
|
-
// ── 2. Date — input field that opens a bottom-sheet ──────────────────────────
|
|
2776
|
-
const [apptDate, setApptDate] = useState<Date | null>(null)
|
|
2777
|
-
|
|
2778
|
-
<StyledDatePicker
|
|
2779
|
-
mode="date"
|
|
2780
|
-
variant="input"
|
|
2781
|
-
label="Appointment date"
|
|
2782
|
-
placeholder="Pick a date"
|
|
2783
|
-
value={apptDate}
|
|
2784
|
-
onChange={setApptDate}
|
|
2785
|
-
onConfirm={setApptDate}
|
|
2786
|
-
confirmLabel="Confirm date"
|
|
2787
|
-
/>
|
|
2788
|
-
|
|
2789
|
-
// ── 3. Date range — inline with indigo fill ───────────────────────────────────
|
|
2790
|
-
// Tap once for start, tap again for end.
|
|
2791
|
-
const [rangeS, setRangeS] = useState<Date | null>(null)
|
|
2792
|
-
const [rangeE, setRangeE] = useState<Date | null>(null)
|
|
2793
|
-
|
|
2794
|
-
<StyledDatePicker
|
|
2795
|
-
mode="range"
|
|
2796
|
-
variant="inline"
|
|
2797
|
-
valueStart={rangeS}
|
|
2798
|
-
valueEnd={rangeE}
|
|
2799
|
-
onRangeChange={(s, e) => { setRangeS(s); setRangeE(e) }}
|
|
2800
|
-
colors={{ selected: '#6366f1', rangeFill: '#eef2ff', today: '#6366f1' }}
|
|
2801
|
-
/>
|
|
2802
|
-
|
|
2803
|
-
// ── 4. Time — scroll drum ─────────────────────────────────────────────────────
|
|
2804
|
-
const [time, setTime] = useState<Date | null>(new Date())
|
|
2805
|
-
|
|
2806
|
-
<StyledDatePicker
|
|
2807
|
-
mode="time"
|
|
2808
|
-
variant="inline"
|
|
2809
|
-
value={time}
|
|
2810
|
-
onChange={setTime}
|
|
2811
|
-
showTodayButton={false}
|
|
2812
|
-
/>
|
|
2813
|
-
|
|
2814
|
-
// ── 5. Date + Time combined ───────────────────────────────────────────────────
|
|
2815
|
-
const [dt, setDt] = useState<Date | null>(new Date())
|
|
2816
|
-
|
|
2817
|
-
<StyledDatePicker
|
|
2818
|
-
mode="datetime"
|
|
2819
|
-
variant="inline"
|
|
2820
|
-
value={dt}
|
|
2821
|
-
onChange={setDt}
|
|
2822
|
-
colors={{ selected: '#0f172a', today: '#6366f1' }}
|
|
2823
|
-
/>
|
|
2824
|
-
|
|
2825
|
-
// ── 6. Month picker ───────────────────────────────────────────────────────────
|
|
2826
|
-
const [month, setMonth] = useState<Date | null>(new Date())
|
|
2827
|
-
|
|
2828
|
-
<StyledDatePicker
|
|
2829
|
-
mode="month"
|
|
2830
|
-
variant="inline"
|
|
2831
|
-
value={month}
|
|
2832
|
-
onChange={setMonth}
|
|
2833
|
-
colors={{ selected: '#059669', today: '#059669' }}
|
|
2834
|
-
/>
|
|
2835
|
-
|
|
2836
|
-
// ── 7. Colour themes ──────────────────────────────────────────────────────────
|
|
2837
|
-
// Lime
|
|
2838
|
-
<StyledDatePicker mode="date" variant="inline" value={lime} onChange={setLime}
|
|
2839
|
-
colors={{ selected: '#8bc34a', today: '#8bc34a', confirmBg: '#8bc34a' }} />
|
|
2840
|
-
|
|
2841
|
-
// Rose
|
|
2842
|
-
<StyledDatePicker mode="date" variant="inline" value={rose} onChange={setRose}
|
|
2843
|
-
colors={{ selected: '#e11d48', today: '#e11d48', rangeFill: '#fff1f2' }} />
|
|
2844
|
-
|
|
2845
|
-
// Indigo
|
|
2846
|
-
<StyledDatePicker mode="date" variant="inline" value={indigo} onChange={setIndigo}
|
|
2847
|
-
colors={{ selected: '#6366f1', today: '#6366f1', rangeFill: '#eef2ff' }} />
|
|
2848
|
-
|
|
2849
|
-
// Amber
|
|
2850
|
-
<StyledDatePicker mode="date" variant="inline" value={amber} onChange={setAmber}
|
|
2851
|
-
colors={{ selected: '#d97706', today: '#d97706', confirmBg: '#d97706' }} />
|
|
2852
|
-
|
|
2853
|
-
// ── 8. Real-world: Hotel booking ──────────────────────────────────────────────
|
|
2854
|
-
// Two input pickers; check-out minDate is bound to check-in.
|
|
2855
|
-
const [checkIn, setCheckIn] = useState<Date | null>(null)
|
|
2856
|
-
const [checkOut, setCheckOut] = useState<Date | null>(null)
|
|
2857
|
-
|
|
2858
|
-
<StyledDatePicker
|
|
2859
|
-
mode="date" variant="input"
|
|
2860
|
-
label="Check-in" placeholder="Select check-in"
|
|
2861
|
-
value={checkIn}
|
|
2862
|
-
onChange={setCheckIn}
|
|
2863
|
-
onConfirm={setCheckIn}
|
|
2864
|
-
minDate={new Date()}
|
|
2865
|
-
colors={{ selected: '#0ea5e9', today: '#0ea5e9', confirmBg: '#0ea5e9' }}
|
|
2866
|
-
/>
|
|
2867
|
-
<StyledDatePicker
|
|
2868
|
-
mode="date" variant="input"
|
|
2869
|
-
label="Check-out" placeholder="Select check-out"
|
|
2870
|
-
value={checkOut}
|
|
2871
|
-
onChange={setCheckOut}
|
|
2872
|
-
onConfirm={setCheckOut}
|
|
2873
|
-
minDate={checkIn ?? new Date()}
|
|
2874
|
-
colors={{ selected: '#0ea5e9', today: '#0ea5e9', confirmBg: '#0ea5e9' }}
|
|
2875
|
-
/>
|
|
2876
|
-
|
|
2877
|
-
// ── 9. Real-world: Event scheduler ───────────────────────────────────────────
|
|
2878
|
-
// Separate date + time inputs, purple theme.
|
|
2879
|
-
const [eventDate, setEventDate] = useState<Date | null>(null)
|
|
2880
|
-
const [eventTime, setEventTime] = useState<Date | null>(null)
|
|
2881
|
-
|
|
2882
|
-
<StyledDatePicker
|
|
2883
|
-
mode="date" variant="input"
|
|
2884
|
-
label="Event date" placeholder="Choose date"
|
|
2885
|
-
value={eventDate} onChange={setEventDate} onConfirm={setEventDate}
|
|
2886
|
-
colors={{ selected: '#8b5cf6', today: '#8b5cf6', confirmBg: '#8b5cf6' }}
|
|
2887
|
-
/>
|
|
2888
|
-
<StyledDatePicker
|
|
2889
|
-
mode="time" variant="input"
|
|
2890
|
-
label="Event time" placeholder="Choose time"
|
|
2891
|
-
value={eventTime} onChange={setEventTime} onConfirm={setEventTime}
|
|
2892
|
-
formatDisplay={(d) => d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}
|
|
2893
|
-
colors={{ selected: '#8b5cf6', today: '#8b5cf6', confirmBg: '#8b5cf6' }}
|
|
2894
|
-
/>
|
|
2895
|
-
|
|
2896
|
-
// ── 10. Real-world: Report period ────────────────────────────────────────────
|
|
2897
|
-
// Month-mode input pickers with amber theme.
|
|
2898
|
-
const [reportFrom, setReportFrom] = useState<Date | null>(null)
|
|
2899
|
-
const [reportTo, setReportTo] = useState<Date | null>(null)
|
|
2900
|
-
|
|
2901
|
-
<StyledDatePicker
|
|
2902
|
-
mode="month" variant="input"
|
|
2903
|
-
label="From month" placeholder="Select start month"
|
|
2904
|
-
value={reportFrom} onChange={setReportFrom} onConfirm={setReportFrom}
|
|
2905
|
-
formatDisplay={(d) => d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
|
|
2906
|
-
colors={{ selected: '#f59e0b', today: '#f59e0b', confirmBg: '#f59e0b' }}
|
|
2907
|
-
/>
|
|
2908
|
-
<StyledDatePicker
|
|
2909
|
-
mode="month" variant="input"
|
|
2910
|
-
label="To month" placeholder="Select end month"
|
|
2911
|
-
value={reportTo} onChange={setReportTo} onConfirm={setReportTo}
|
|
2912
|
-
formatDisplay={(d) => d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}
|
|
2913
|
-
colors={{ selected: '#f59e0b', today: '#f59e0b', confirmBg: '#f59e0b' }}
|
|
2914
|
-
/>
|
|
2915
|
-
```
|
|
2916
|
-
|
|
2917
|
-
---
|
|
2918
|
-
|
|
2919
|
-
### StyledBottomSheet
|
|
2920
|
-
|
|
2921
|
-
Gesture-driven bottom sheet built on `PanResponder` with spring physics, configurable snap points, backdrop, scrollable content, and built-in header. Mounts inside a `Modal` — no portal required.
|
|
2922
|
-
|
|
2923
|
-
```tsx
|
|
2924
|
-
import { StyledBottomSheet } from 'fluent-styles'
|
|
2925
|
-
```
|
|
2926
|
-
|
|
2927
|
-
#### Variants
|
|
2928
|
-
|
|
2929
|
-
| Variant | Description |
|
|
2930
|
-
|---|---|
|
|
2931
|
-
| `default` | Standard bottom sheet sliding up from the bottom |
|
|
2932
|
-
| `modal` | Full-modal style with stronger overlay |
|
|
2933
|
-
| `sidebar` | Slides in from the side (landscape / tablet) |
|
|
2934
|
-
|
|
2935
|
-
#### Themes
|
|
2936
|
-
|
|
2937
|
-
| Theme | Surface |
|
|
2938
|
-
|---|---|
|
|
2939
|
-
| `light` | White background, gray handle (default) |
|
|
2940
|
-
| `dark` | `gray[900]` background, lighter handle and text |
|
|
2941
|
-
|
|
2942
|
-
#### `BottomSheetColors`
|
|
2943
|
-
|
|
2944
|
-
| Field | Default (light) | Description |
|
|
2945
|
-
|---|---|---|
|
|
2946
|
-
| `background` | `white` | Sheet surface colour |
|
|
2947
|
-
| `overlay` | `rgba(0,0,0,0.45)` | Backdrop tint |
|
|
2948
|
-
| `handle` | `gray[300]` | Drag handle pill |
|
|
2949
|
-
| `headerBorder` | `gray[100]` | Line under header |
|
|
2950
|
-
| `headerTitle` | `gray[900]` | Title text |
|
|
2951
|
-
| `headerSub` | `gray[400]` | Subtitle text |
|
|
2952
|
-
| `closeIconBg` | `gray[100]` | Close button background |
|
|
2953
|
-
| `closeIcon` | `gray[600]` | Close button icon colour |
|
|
2954
|
-
|
|
2955
|
-
#### Props
|
|
2956
|
-
|
|
2957
|
-
| Prop | Type | Default | Description |
|
|
2958
|
-
|---|---|---|---|
|
|
2959
|
-
| `visible` | `boolean` | required | Controls sheet visibility |
|
|
2960
|
-
| `onClose` | `() => void` | required | Called when sheet should close |
|
|
2961
|
-
| `snapPoints` | `(number \| string)[]` | `['40%','75%']` | Snap heights in px or `'50%'` percent strings |
|
|
2962
|
-
| `initialSnap` | `number` | `0` | Index of snap point to open at |
|
|
2963
|
-
| `title` | `ReactNode` | — | Header title |
|
|
2964
|
-
| `subtitle` | `ReactNode` | — | Header subtitle |
|
|
2965
|
-
| `showHandle` | `boolean` | `true` | Show drag handle pill |
|
|
2966
|
-
| `showClose` | `boolean` | `false` | Show ✕ close button |
|
|
2967
|
-
| `closeOnBackdrop` | `boolean` | `true` | Tap backdrop to close |
|
|
2968
|
-
| `destroyOnClose` | `boolean` | `false` | Unmount children when hidden |
|
|
2969
|
-
| `scrollable` | `boolean` | `false` | Wrap content in a `ScrollView` |
|
|
2970
|
-
| `variant` | `BottomSheetVariant` | `'default'` | Visual variant |
|
|
2971
|
-
| `sheetTheme` | `BottomSheetTheme` | `'light'` | Built-in colour theme |
|
|
2972
|
-
| `colors` | `Partial<BottomSheetColors>` | — | Per-token colour overrides |
|
|
2973
|
-
| `borderRadius` | `number` | `20` | Top corner radius |
|
|
2974
|
-
| `onOpen` | `() => void` | — | Fires when open animation starts |
|
|
2975
|
-
| `onOpened` | `() => void` | — | Fires when open animation ends |
|
|
2976
|
-
| `onClosed` | `() => void` | — | Fires when close animation ends |
|
|
2977
|
-
|
|
2978
|
-
#### Usage
|
|
2979
|
-
|
|
2980
|
-
```tsx
|
|
2981
|
-
import React, { useState } from 'react'
|
|
2982
|
-
import { StyledBottomSheet, Stack, StyledText, StyledPressable } from 'fluent-styles'
|
|
2983
|
-
|
|
2984
|
-
const [visible, setVisible] = useState(false)
|
|
2985
|
-
|
|
2986
|
-
// ── 1. Plain sheet ────────────────────────────────────────────────────────────
|
|
2987
|
-
<StyledBottomSheet visible={visible} onClose={() => setVisible(false)}>
|
|
2988
|
-
<Stack padding={24} gap={8}>
|
|
2989
|
-
<StyledText fontSize={16} fontWeight="700">Plain sheet</StyledText>
|
|
2990
|
-
<StyledText fontSize={14} color="#6b7280">No header — just your content.</StyledText>
|
|
2991
|
-
</Stack>
|
|
2992
|
-
</StyledBottomSheet>
|
|
2993
|
-
|
|
2994
|
-
// ── 2. With title + subtitle ──────────────────────────────────────────────────
|
|
2995
|
-
<StyledBottomSheet
|
|
2996
|
-
visible={visible}
|
|
2997
|
-
onClose={() => setVisible(false)}
|
|
2998
|
-
title="Notifications"
|
|
2999
|
-
subtitle="Your recent alerts"
|
|
3000
|
-
showClose
|
|
3001
|
-
>
|
|
3002
|
-
<Stack padding={20} gap={12}>{/* content */}</Stack>
|
|
3003
|
-
</StyledBottomSheet>
|
|
3004
|
-
|
|
3005
|
-
// ── 3. Multiple snap points ───────────────────────────────────────────────────
|
|
3006
|
-
// Drag between 25 %, 50 %, and 85 % of screen height.
|
|
3007
|
-
<StyledBottomSheet
|
|
3008
|
-
visible={visible}
|
|
3009
|
-
onClose={() => setVisible(false)}
|
|
3010
|
-
title="Three snaps"
|
|
3011
|
-
showClose
|
|
3012
|
-
snapPoints={['25%', '50%', '85%']}
|
|
3013
|
-
>
|
|
3014
|
-
<Stack padding={20}>{/* content */}</Stack>
|
|
3015
|
-
</StyledBottomSheet>
|
|
3016
|
-
|
|
3017
|
-
// ── 4. Scrollable content ─────────────────────────────────────────────────────
|
|
3018
|
-
<StyledBottomSheet
|
|
3019
|
-
visible={visible}
|
|
3020
|
-
onClose={() => setVisible(false)}
|
|
3021
|
-
title="Long list"
|
|
3022
|
-
showClose
|
|
3023
|
-
scrollable
|
|
3024
|
-
snapPoints={['60%']}
|
|
3025
|
-
>
|
|
3026
|
-
<Stack padding={20} gap={10}>
|
|
3027
|
-
{Array.from({ length: 30 }).map((_, i) => (
|
|
3028
|
-
<StyledText key={i}>Item {i + 1}</StyledText>
|
|
3029
|
-
))}
|
|
3030
|
-
</Stack>
|
|
3031
|
-
</StyledBottomSheet>
|
|
3032
|
-
|
|
3033
|
-
// ── 5. Dark theme ─────────────────────────────────────────────────────────────
|
|
3034
|
-
<StyledBottomSheet
|
|
3035
|
-
visible={visible}
|
|
3036
|
-
onClose={() => setVisible(false)}
|
|
3037
|
-
title="Dark sheet"
|
|
3038
|
-
subtitle="Midnight surface"
|
|
3039
|
-
showClose
|
|
3040
|
-
sheetTheme="dark"
|
|
3041
|
-
>
|
|
3042
|
-
<Stack padding={20} gap={12}>{/* content */}</Stack>
|
|
3043
|
-
</StyledBottomSheet>
|
|
3044
|
-
|
|
3045
|
-
// ── 6. Colour token overrides (indigo) ────────────────────────────────────────
|
|
3046
|
-
<StyledBottomSheet
|
|
3047
|
-
visible={visible}
|
|
3048
|
-
onClose={() => setVisible(false)}
|
|
3049
|
-
title="Indigo theme"
|
|
3050
|
-
showClose
|
|
3051
|
-
colors={{
|
|
3052
|
-
background: '#eef2ff',
|
|
3053
|
-
handle: '#a5b4fc',
|
|
3054
|
-
headerTitle: '#312e81',
|
|
3055
|
-
headerBorder: '#c7d2fe',
|
|
3056
|
-
closeIconBg: '#c7d2fe',
|
|
3057
|
-
closeIcon: '#4f46e5',
|
|
3058
|
-
}}
|
|
3059
|
-
>
|
|
3060
|
-
<Stack padding={20}>{/* content */}</Stack>
|
|
3061
|
-
</StyledBottomSheet>
|
|
3062
|
-
|
|
3063
|
-
// ── 7. Real-world: share sheet ────────────────────────────────────────────────
|
|
3064
|
-
<StyledBottomSheet
|
|
3065
|
-
visible={shareOpen}
|
|
3066
|
-
onClose={() => setShareOpen(false)}
|
|
3067
|
-
title="Share post"
|
|
3068
|
-
subtitle="Choose where to send"
|
|
3069
|
-
showClose
|
|
3070
|
-
snapPoints={['42%']}
|
|
3071
|
-
>
|
|
3072
|
-
<Stack padding={20} gap={16}>
|
|
3073
|
-
<Stack horizontal gap={20} justifyContent="center">
|
|
3074
|
-
{[{ icon: '💬', label: 'Messages' }, { icon: '✉️', label: 'Mail' },
|
|
3075
|
-
{ icon: '🔗', label: 'Copy link' }, { icon: '📋', label: 'More' }]
|
|
3076
|
-
.map(({ icon, label }) => (
|
|
3077
|
-
<Stack key={label} alignItems="center" gap={6}>
|
|
3078
|
-
<StyledPressable width={52} height={52} borderRadius={26}
|
|
3079
|
-
backgroundColor="#f3f4f6" alignItems="center" justifyContent="center"
|
|
3080
|
-
onPress={() => setShareOpen(false)}>
|
|
3081
|
-
<StyledText fontSize={22}>{icon}</StyledText>
|
|
3082
|
-
</StyledPressable>
|
|
3083
|
-
<StyledText fontSize={11} color="#9ca3af">{label}</StyledText>
|
|
3084
|
-
</Stack>
|
|
3085
|
-
))}
|
|
3086
|
-
</Stack>
|
|
3087
|
-
</Stack>
|
|
3088
|
-
</StyledBottomSheet>
|
|
3089
|
-
|
|
3090
|
-
// ── 8. Real-world: options menu ───────────────────────────────────────────────
|
|
3091
|
-
<StyledBottomSheet
|
|
3092
|
-
visible={optionsOpen}
|
|
3093
|
-
onClose={() => setOptionsOpen(false)}
|
|
3094
|
-
title="Post options"
|
|
3095
|
-
showClose
|
|
3096
|
-
snapPoints={['48%']}
|
|
3097
|
-
>
|
|
3098
|
-
<Stack paddingHorizontal={20} paddingBottom={16}>
|
|
3099
|
-
{[
|
|
3100
|
-
{ icon: '✏️', label: 'Edit post', color: '#1f2937' },
|
|
3101
|
-
{ icon: '🔗', label: 'Copy link', color: '#1f2937' },
|
|
3102
|
-
{ icon: '🗑️', label: 'Delete post', color: '#e11d48' },
|
|
3103
|
-
].map(({ icon, label, color }, idx, arr) => (
|
|
3104
|
-
<Stack key={label}>
|
|
3105
|
-
<StyledPressable flexDirection="row" gap={14} paddingVertical={14} alignItems="center"
|
|
3106
|
-
onPress={() => setOptionsOpen(false)}>
|
|
3107
|
-
<StyledText fontSize={20}>{icon}</StyledText>
|
|
3108
|
-
<StyledText fontSize={15} fontWeight="600" color={color}>{label}</StyledText>
|
|
3109
|
-
</StyledPressable>
|
|
3110
|
-
{idx < arr.length - 1 && <StyledDivider borderBottomColor="#f3f4f6" />}
|
|
3111
|
-
</Stack>
|
|
3112
|
-
))}
|
|
3113
|
-
</Stack>
|
|
3114
|
-
</StyledBottomSheet>
|
|
3115
|
-
|
|
3116
|
-
// ── 9. Locked sheet (no backdrop dismiss) ─────────────────────────────────────
|
|
3117
|
-
<StyledBottomSheet
|
|
3118
|
-
visible={visible}
|
|
3119
|
-
onClose={() => setVisible(false)}
|
|
3120
|
-
title="Locked"
|
|
3121
|
-
showClose
|
|
3122
|
-
closeOnBackdrop={false}
|
|
3123
|
-
>
|
|
3124
|
-
<Stack padding={20}>
|
|
3125
|
-
<StyledText>Use the ✕ button to dismiss — backdrop tap is disabled.</StyledText>
|
|
3126
|
-
</Stack>
|
|
3127
|
-
</StyledBottomSheet>
|
|
3128
|
-
```
|
|
3129
|
-
|
|
3130
|
-
---
|
|
3131
|
-
|
|
3132
|
-
### StyledEmptyState
|
|
3133
|
-
|
|
3134
|
-
Animated zero-state / empty-data display with 5 variants, an illustration slot (emoji, icon, or any `ReactNode`), primary + secondary action buttons, fade-in entrance animation, and a compact horizontal layout mode.
|
|
3135
|
-
|
|
3136
|
-
```tsx
|
|
3137
|
-
import { StyledEmptyState } from 'fluent-styles'
|
|
3138
|
-
```
|
|
3139
|
-
|
|
3140
|
-
#### Variants
|
|
3141
|
-
|
|
3142
|
-
| Variant | Description |
|
|
3143
|
-
|---|---|
|
|
3144
|
-
| `default` | Centred illustration + title + description + actions |
|
|
3145
|
-
| `card` | Same as default, rendered inside a subtle card surface |
|
|
3146
|
-
| `minimal` | Compact — small emoji, lighter text, single button |
|
|
3147
|
-
| `illustrated` | Large illustration circle, bolder visual weight |
|
|
3148
|
-
| `action` | Full-bleed coloured panel, designed for CTA prominence |
|
|
3149
|
-
|
|
3150
|
-
#### `EmptyStateColors`
|
|
3151
|
-
|
|
3152
|
-
| Field | Default | Description |
|
|
3153
|
-
|---|---|---|
|
|
3154
|
-
| `background` | `transparent` | Container background |
|
|
3155
|
-
| `illustrationBg` | `gray[100]` | Illustration circle fill |
|
|
3156
|
-
| `title` | `gray[900]` | Title text colour |
|
|
3157
|
-
| `description` | `gray[400]` | Description text colour |
|
|
3158
|
-
| `primaryBg` | `gray[900]` | Primary button background |
|
|
3159
|
-
| `primaryText` | `white` | Primary button text |
|
|
3160
|
-
| `primaryBorder` | `gray[900]` | Primary button border |
|
|
3161
|
-
| `secondaryBg` | `transparent` | Secondary button background |
|
|
3162
|
-
| `secondaryText` | `gray[700]` | Secondary button text |
|
|
3163
|
-
| `secondaryBorder` | `gray[200]` | Secondary button border |
|
|
3164
|
-
| `border` | `gray[100]` | Card variant border |
|
|
3165
|
-
|
|
3166
|
-
#### Props
|
|
3167
|
-
|
|
3168
|
-
| Prop | Type | Default | Description |
|
|
3169
|
-
|---|---|---|---|
|
|
3170
|
-
| `variant` | `EmptyStateVariant` | `'default'` | Visual style |
|
|
3171
|
-
| `illustration` | `ReactNode` | — | Emoji string, icon component, or any node |
|
|
3172
|
-
| `title` | `string` | — | Heading text |
|
|
3173
|
-
| `description` | `string` | — | Supporting body text |
|
|
3174
|
-
| `actions` | `EmptyStateAction[]` | `[]` | Array of action buttons |
|
|
3175
|
-
| `compact` | `boolean` | `false` | Horizontal layout for banners / list cells |
|
|
3176
|
-
| `animated` | `boolean` | `true` | Fade + slide-up entrance animation |
|
|
3177
|
-
| `colors` | `Partial<EmptyStateColors>` | — | Colour overrides |
|
|
3178
|
-
| `padding` | `number` | `32` | Padding around the container |
|
|
3179
|
-
|
|
3180
|
-
`EmptyStateAction` shape: `{ label: string; onPress: () => void; icon?: ReactNode; variant?: 'primary' | 'secondary' }`
|
|
3181
|
-
|
|
3182
|
-
#### Usage
|
|
3183
|
-
|
|
3184
|
-
```tsx
|
|
3185
|
-
import { StyledEmptyState } from 'fluent-styles'
|
|
3186
|
-
|
|
3187
|
-
// ── 1. Default ────────────────────────────────────────────────────────────────
|
|
3188
|
-
<StyledEmptyState
|
|
3189
|
-
illustration="📭"
|
|
3190
|
-
title="No messages yet"
|
|
3191
|
-
description="When you receive a message, it'll show up here."
|
|
3192
|
-
actions={[{ label: 'Start a conversation', onPress: () => {} }]}
|
|
3193
|
-
/>
|
|
3194
|
-
|
|
3195
|
-
// ── 2. Card variant with two actions ─────────────────────────────────────────
|
|
3196
|
-
<StyledEmptyState
|
|
3197
|
-
variant="card"
|
|
3198
|
-
illustration="🔍"
|
|
3199
|
-
title="No results found"
|
|
3200
|
-
description="Try adjusting your search or filters."
|
|
3201
|
-
actions={[
|
|
3202
|
-
{ label: 'Clear filters', onPress: () => {}, variant: 'secondary' },
|
|
3203
|
-
{ label: 'Try different terms', onPress: () => {}, variant: 'primary' },
|
|
3204
|
-
]}
|
|
3205
|
-
/>
|
|
3206
|
-
|
|
3207
|
-
// ── 3. Minimal ────────────────────────────────────────────────────────────────
|
|
3208
|
-
<StyledEmptyState
|
|
3209
|
-
variant="minimal"
|
|
3210
|
-
illustration="🌿"
|
|
3211
|
-
title="Nothing here yet"
|
|
3212
|
-
description="Add your first item to get started."
|
|
3213
|
-
actions={[{ label: '+ Add item', onPress: () => {} }]}
|
|
3214
|
-
/>
|
|
3215
|
-
|
|
3216
|
-
// ── 4. Illustrated ────────────────────────────────────────────────────────────
|
|
3217
|
-
<StyledEmptyState
|
|
3218
|
-
variant="illustrated"
|
|
3219
|
-
illustration="🗂️"
|
|
3220
|
-
title="No files uploaded"
|
|
3221
|
-
description="Drop your documents here or tap the button below."
|
|
3222
|
-
actions={[
|
|
3223
|
-
{ label: 'Upload file', onPress: () => {}, variant: 'primary' },
|
|
3224
|
-
{ label: 'Browse Drive', onPress: () => {}, variant: 'secondary' },
|
|
3225
|
-
]}
|
|
3226
|
-
/>
|
|
3227
|
-
|
|
3228
|
-
// ── 5. Action-focused with colour override ────────────────────────────────────
|
|
3229
|
-
<StyledEmptyState
|
|
3230
|
-
variant="action"
|
|
3231
|
-
illustration="🚀"
|
|
3232
|
-
title="Ready to launch?"
|
|
3233
|
-
description="Set up your first project and deploy in seconds."
|
|
3234
|
-
actions={[{ label: 'Create project', onPress: () => {} }]}
|
|
3235
|
-
colors={{
|
|
3236
|
-
background: '#4f46e5',
|
|
3237
|
-
illustrationBg: 'rgba(255,255,255,0.15)',
|
|
3238
|
-
title: '#ffffff',
|
|
3239
|
-
description: 'rgba(255,255,255,0.75)',
|
|
3240
|
-
primaryBg: '#ffffff',
|
|
3241
|
-
primaryText: '#4338ca',
|
|
3242
|
-
primaryBorder: '#ffffff',
|
|
3243
|
-
}}
|
|
3244
|
-
/>
|
|
3245
|
-
|
|
3246
|
-
// ── 6. Compact (horizontal banner) ───────────────────────────────────────────
|
|
3247
|
-
<StyledEmptyState
|
|
3248
|
-
compact
|
|
3249
|
-
illustration="📬"
|
|
3250
|
-
title="No notifications"
|
|
3251
|
-
description="You're all caught up."
|
|
3252
|
-
actions={[{ label: 'Mark all read', onPress: () => {} }]}
|
|
3253
|
-
/>
|
|
3254
|
-
|
|
3255
|
-
// ── 7. No animation ───────────────────────────────────────────────────────────
|
|
3256
|
-
<StyledEmptyState
|
|
3257
|
-
illustration="📋"
|
|
3258
|
-
title="Empty"
|
|
3259
|
-
animated={false}
|
|
3260
|
-
/>
|
|
3261
|
-
```
|
|
3262
|
-
|
|
3263
|
-
---
|
|
3264
|
-
|
|
3265
|
-
### StyledSearchBar
|
|
3266
|
-
|
|
3267
|
-
Animated search input with focus ring, clear button, cancel, suggestion dropdown, and custom left icon / right action slots.
|
|
3268
|
-
|
|
3269
|
-
```tsx
|
|
3270
|
-
import { StyledSearchBar } from 'fluent-styles'
|
|
3271
|
-
```
|
|
3272
|
-
|
|
3273
|
-
#### Variants
|
|
3274
|
-
|
|
3275
|
-
| Variant | Description |
|
|
3276
|
-
|---|---|
|
|
3277
|
-
| `filled` | Solid grey background, no border (default) |
|
|
3278
|
-
| `outline` | Transparent background, visible border |
|
|
3279
|
-
| `ghost` | No background or border — blends into any surface |
|
|
3280
|
-
| `floating` | Drop-shadow, white background |
|
|
3281
|
-
|
|
3282
|
-
#### Sizes
|
|
3283
|
-
|
|
3284
|
-
| Size | Height | Font | Icon | Border radius |
|
|
3285
|
-
|---|---|---|---|---|
|
|
3286
|
-
| `sm` | 36 px | 13 px | 14 px | 10 px |
|
|
3287
|
-
| `md` | 44 px (default) | 15 px | 16 px | 12 px |
|
|
3288
|
-
| `lg` | 52 px | 17 px | 18 px | 14 px |
|
|
3289
|
-
|
|
3290
|
-
#### `SearchBarColors`
|
|
3291
|
-
|
|
3292
|
-
| Field | Default | Description |
|
|
3293
|
-
|---|---|---|
|
|
3294
|
-
| `background` | `gray[100]` | Input container fill |
|
|
3295
|
-
| `border` | `gray[200]` | Resting border |
|
|
3296
|
-
| `focusBorder` | `gray[900]` | Focused border |
|
|
3297
|
-
| `placeholder` | `gray[400]` | Placeholder text |
|
|
3298
|
-
| `text` | `gray[900]` | Input text |
|
|
3299
|
-
| `icon` | `gray[400]` | Search icon |
|
|
3300
|
-
| `clearBg` | `gray[300]` | Clear button background |
|
|
3301
|
-
| `clearIcon` | `gray[600]` | Clear button icon |
|
|
3302
|
-
| `cancelText` | `gray[900]` | Cancel button label |
|
|
3303
|
-
| `suggestionBg` | `white` | Suggestion dropdown background |
|
|
3304
|
-
| `suggestionText` | `gray[800]` | Suggestion item text |
|
|
3305
|
-
| `suggestionBorder` | `gray[100]` | Suggestion row divider |
|
|
3306
|
-
| `divider` | `gray[100]` | Divider between suggestions |
|
|
3307
|
-
|
|
3308
|
-
#### Props
|
|
3309
|
-
|
|
3310
|
-
| Prop | Type | Default | Description |
|
|
3311
|
-
|---|---|---|---|
|
|
3312
|
-
| `variant` | `SearchBarVariant` | `'filled'` | Visual style |
|
|
3313
|
-
| `size` | `SearchBarSize` | `'md'` | Height / font preset |
|
|
3314
|
-
| `placeholder` | `string` | `'Search…'` | Placeholder text |
|
|
3315
|
-
| `value` | `string` | — | Controlled value |
|
|
3316
|
-
| `onChangeText` | `(text: string) => void` | — | Text change handler |
|
|
3317
|
-
| `onSubmit` | `(text: string) => void` | — | Submit / search action |
|
|
3318
|
-
| `onCancel` | `() => void` | — | Cancel button handler |
|
|
3319
|
-
| `onClear` | `() => void` | — | Clear button handler |
|
|
3320
|
-
| `showCancel` | `boolean` | `false` | Show cancel button on focus |
|
|
3321
|
-
| `cancelLabel` | `string` | `'Cancel'` | Cancel button label |
|
|
3322
|
-
| `leftIcon` | `ReactNode` | search icon | Custom left icon |
|
|
3323
|
-
| `rightAction` | `ReactNode` | — | Right-side action slot (filter, voice, etc.) |
|
|
3324
|
-
| `suggestions` | `SearchSuggestion[]` | — | Dropdown suggestion items |
|
|
3325
|
-
| `onSuggestionPress` | `(item: SearchSuggestion) => void` | — | Suggestion tap handler |
|
|
3326
|
-
| `loading` | `boolean` | `false` | Replaces icon with spinner |
|
|
3327
|
-
| `disabled` | `boolean` | `false` | Disables input |
|
|
3328
|
-
| `autoFocus` | `boolean` | `false` | Focus on mount |
|
|
3329
|
-
| `colors` | `Partial<SearchBarColors>` | — | Colour overrides |
|
|
3330
|
-
| `borderRadius` | `number` | size preset | Border radius override |
|
|
3331
|
-
|
|
3332
|
-
`SearchSuggestion` shape: `{ id: string; label: string; subtitle?: string; icon?: ReactNode }`
|
|
3333
|
-
|
|
3334
|
-
Accepts all `TextInputProps` (except `style`).
|
|
3335
|
-
|
|
3336
|
-
#### Usage
|
|
3337
|
-
|
|
3338
|
-
```tsx
|
|
3339
|
-
import React, { useState } from 'react'
|
|
3340
|
-
import { StyledSearchBar, type SearchSuggestion } from 'fluent-styles'
|
|
3341
|
-
|
|
3342
|
-
const [query, setQuery] = useState('')
|
|
3343
|
-
|
|
3344
|
-
// ── 1. Variants ───────────────────────────────────────────────────────────────
|
|
3345
|
-
<StyledSearchBar variant="filled" value={query} onChangeText={setQuery} />
|
|
3346
|
-
<StyledSearchBar variant="outline" value={query} onChangeText={setQuery} />
|
|
3347
|
-
<StyledSearchBar variant="ghost" value={query} onChangeText={setQuery} />
|
|
3348
|
-
<StyledSearchBar variant="floating" value={query} onChangeText={setQuery} />
|
|
3349
|
-
|
|
3350
|
-
// ── 2. Sizes ──────────────────────────────────────────────────────────────────
|
|
3351
|
-
<StyledSearchBar size="sm" placeholder="Small" />
|
|
3352
|
-
<StyledSearchBar size="md" placeholder="Medium (default)" />
|
|
3353
|
-
<StyledSearchBar size="lg" placeholder="Large" />
|
|
3354
|
-
|
|
3355
|
-
// ── 3. Cancel button ──────────────────────────────────────────────────────────
|
|
3356
|
-
<StyledSearchBar
|
|
3357
|
-
value={query}
|
|
3358
|
-
onChangeText={setQuery}
|
|
3359
|
-
showCancel
|
|
3360
|
-
onCancel={() => setQuery('')}
|
|
3361
|
-
/>
|
|
3362
|
-
|
|
3363
|
-
// ── 4. Suggestion dropdown ────────────────────────────────────────────────────
|
|
3364
|
-
const SUGGESTIONS: SearchSuggestion[] = [
|
|
3365
|
-
{ id: '1', label: 'React Native', subtitle: 'Framework' },
|
|
3366
|
-
{ id: '2', label: 'TypeScript', subtitle: 'Language' },
|
|
3367
|
-
]
|
|
3368
|
-
|
|
3369
|
-
<StyledSearchBar
|
|
3370
|
-
variant="outline"
|
|
3371
|
-
value={query}
|
|
3372
|
-
onChangeText={setQuery}
|
|
3373
|
-
suggestions={SUGGESTIONS.filter(s =>
|
|
3374
|
-
s.label.toLowerCase().includes(query.toLowerCase())
|
|
3375
|
-
)}
|
|
3376
|
-
onSuggestionPress={(item) => setQuery(item.label)}
|
|
3377
|
-
showCancel
|
|
3378
|
-
onCancel={() => setQuery('')}
|
|
3379
|
-
/>
|
|
3380
|
-
|
|
3381
|
-
// ── 5. Right action slot (filter / voice) ─────────────────────────────────────
|
|
3382
|
-
<StyledSearchBar
|
|
3383
|
-
placeholder="Search with filter…"
|
|
3384
|
-
rightAction={
|
|
3385
|
-
<StyledPressable onPress={() => {}} paddingHorizontal={8} paddingVertical={4}
|
|
3386
|
-
borderRadius={8} backgroundColor="#f3f4f6">
|
|
3387
|
-
<StyledText fontSize={14}>⚙️</StyledText>
|
|
3388
|
-
</StyledPressable>
|
|
3389
|
-
}
|
|
3390
|
-
/>
|
|
3391
|
-
|
|
3392
|
-
// ── 6. Custom left icon ───────────────────────────────────────────────────────
|
|
3393
|
-
<StyledSearchBar
|
|
3394
|
-
placeholder="Search locations…"
|
|
3395
|
-
leftIcon={<StyledText fontSize={16}>📍</StyledText>}
|
|
3396
|
-
/>
|
|
3397
|
-
|
|
3398
|
-
// ── 7. Loading & disabled states ──────────────────────────────────────────────
|
|
3399
|
-
<StyledSearchBar variant="filled" value="react native" loading />
|
|
3400
|
-
<StyledSearchBar variant="outline" value="disabled" disabled />
|
|
3401
|
-
|
|
3402
|
-
// ── 8. Dark theme colour override ─────────────────────────────────────────────
|
|
3403
|
-
<StyledSearchBar
|
|
3404
|
-
variant="filled"
|
|
3405
|
-
value={query}
|
|
3406
|
-
onChangeText={setQuery}
|
|
3407
|
-
colors={{
|
|
3408
|
-
background: '#1f2937',
|
|
3409
|
-
border: '#374151',
|
|
3410
|
-
focusBorder: '#818cf8',
|
|
3411
|
-
placeholder: '#6b7280',
|
|
3412
|
-
text: '#f9fafb',
|
|
3413
|
-
icon: '#6b7280',
|
|
3414
|
-
clearBg: '#4b5563',
|
|
3415
|
-
clearIcon: '#e5e7eb',
|
|
3416
|
-
cancelText: '#f9fafb',
|
|
3417
|
-
suggestionBg: '#1f2937',
|
|
3418
|
-
suggestionText: '#f9fafb',
|
|
3419
|
-
suggestionBorder: '#374151',
|
|
3420
|
-
}}
|
|
3421
|
-
/>
|
|
3422
|
-
```
|
|
3423
|
-
|
|
3424
|
-
---
|
|
3425
|
-
|
|
3426
|
-
### StyledSkeleton
|
|
3427
|
-
|
|
3428
|
-
Animated loading placeholder with shimmer and pulse animations, 4 primitive shapes, 5 pre-built layout templates, and a `SkeletonBone` export for custom compositions.
|
|
3429
|
-
|
|
3430
|
-
```tsx
|
|
3431
|
-
import { StyledSkeleton, SkeletonBone } from 'fluent-styles'
|
|
3432
|
-
```
|
|
3433
|
-
|
|
3434
|
-
#### Templates
|
|
3435
|
-
|
|
3436
|
-
Pre-composed layouts that reflect common UI patterns. Pass `repeat` to stack multiple copies.
|
|
3437
|
-
|
|
3438
|
-
| Template | Layout |
|
|
3439
|
-
|---|---|
|
|
3440
|
-
| `card` | Banner image + title line + two text lines + avatar row |
|
|
3441
|
-
| `list-item` | Circle avatar + two text lines |
|
|
3442
|
-
| `profile` | Large avatar + name + subtitle + stats row |
|
|
3443
|
-
| `article` | Full-width image + three text lines |
|
|
3444
|
-
| `grid` | 2 × 2 card grid |
|
|
3445
|
-
|
|
3446
|
-
#### Shapes (primitives)
|
|
3447
|
-
|
|
3448
|
-
| Shape | Border radius |
|
|
3449
|
-
|---|---|
|
|
3450
|
-
| `rect` | `0` — sharp corners |
|
|
3451
|
-
| `rounded` | `8` px |
|
|
3452
|
-
| `text` | `4` px — matches typical text line height |
|
|
3453
|
-
| `circle` | `width / 2` — perfect circle |
|
|
3454
|
-
|
|
3455
|
-
#### Animation types
|
|
3456
|
-
|
|
3457
|
-
| Value | Behaviour |
|
|
3458
|
-
|---|---|
|
|
3459
|
-
| `shimmer` | Horizontal highlight sweep (default) |
|
|
3460
|
-
| `pulse` | Opacity fade in/out |
|
|
3461
|
-
| `none` | Static — no animation |
|
|
3462
|
-
|
|
3463
|
-
#### `SkeletonColors`
|
|
3464
|
-
|
|
3465
|
-
| Field | Default (light) | Description |
|
|
3466
|
-
|---|---|---|
|
|
3467
|
-
| `base` | `gray[100]` | Bone background colour |
|
|
3468
|
-
| `highlight` | `gray[50]` | Shimmer highlight colour |
|
|
3469
|
-
| `shimmer` | `rgba(255,255,255,0.65)` | Shimmer overlay |
|
|
3470
|
-
|
|
3471
|
-
#### Props
|
|
3472
|
-
|
|
3473
|
-
| Prop | Type | Default | Description |
|
|
3474
|
-
|---|---|---|---|
|
|
3475
|
-
| `width` | `number \| \`${number}%\`` | — | Bone width (primitive mode) |
|
|
3476
|
-
| `height` | `number` | — | Bone height (primitive mode) |
|
|
3477
|
-
| `shape` | `SkeletonShape` | `'rounded'` | Bone shape |
|
|
3478
|
-
| `borderRadius` | `number` | shape default | Custom border radius |
|
|
3479
|
-
| `template` | `SkeletonTemplate` | — | Render a pre-built layout |
|
|
3480
|
-
| `repeat` | `number` | `1` | Repeat template N times |
|
|
3481
|
-
| `animation` | `SkeletonAnimation` | `'shimmer'` | Animation type |
|
|
3482
|
-
| `speed` | `number` | `1400` | Cycle duration in ms — lower = faster |
|
|
3483
|
-
| `skeletonTheme` | `SkeletonTheme` | `'light'` | Built-in colour theme |
|
|
3484
|
-
| `colors` | `Partial<SkeletonColors>` | — | Per-token colour overrides |
|
|
3485
|
-
|
|
3486
|
-
#### Usage
|
|
3487
|
-
|
|
3488
|
-
```tsx
|
|
3489
|
-
import { StyledSkeleton, SkeletonBone } from 'fluent-styles'
|
|
3490
|
-
|
|
3491
|
-
// ── 1. Templates ──────────────────────────────────────────────────────────────
|
|
3492
|
-
<StyledSkeleton template="card" animation="shimmer" />
|
|
3493
|
-
<StyledSkeleton template="list-item" animation="shimmer" repeat={3} />
|
|
3494
|
-
<StyledSkeleton template="profile" animation="shimmer" />
|
|
3495
|
-
<StyledSkeleton template="article" animation="shimmer" />
|
|
3496
|
-
<StyledSkeleton template="grid" animation="shimmer" />
|
|
3497
|
-
|
|
3498
|
-
// ── 2. Toggle with loaded content ─────────────────────────────────────────────
|
|
3499
|
-
const [loading, setLoading] = useState(true)
|
|
3500
|
-
|
|
3501
|
-
{loading
|
|
3502
|
-
? <StyledSkeleton template="card" animation="shimmer" />
|
|
3503
|
-
: <MyContentCard />
|
|
3504
|
-
}
|
|
3505
|
-
|
|
3506
|
-
// ── 3. Primitive shapes ───────────────────────────────────────────────────────
|
|
3507
|
-
// Text lines
|
|
3508
|
-
<StyledSkeleton width="100%" height={13} shape="text" animation="shimmer" />
|
|
3509
|
-
<StyledSkeleton width="85%" height={13} shape="text" animation="shimmer" />
|
|
3510
|
-
<StyledSkeleton width="65%" height={13} shape="text" animation="shimmer" />
|
|
3511
|
-
|
|
3512
|
-
// Banner image
|
|
3513
|
-
<StyledSkeleton width="100%" height={160} shape="rounded" animation="shimmer" />
|
|
3514
|
-
|
|
3515
|
-
// Avatars
|
|
3516
|
-
<StyledSkeleton width={48} height={48} shape="circle" animation="shimmer" />
|
|
3517
|
-
<StyledSkeleton width={64} height={64} shape="circle" animation="shimmer" />
|
|
3518
|
-
|
|
3519
|
-
// ── 4. Animation types ────────────────────────────────────────────────────────
|
|
3520
|
-
<StyledSkeleton width="100%" height={14} animation="shimmer" />
|
|
3521
|
-
<StyledSkeleton width="100%" height={14} animation="pulse" />
|
|
3522
|
-
<StyledSkeleton width="100%" height={14} animation="none" />
|
|
3523
|
-
|
|
3524
|
-
// ── 5. Speed control ──────────────────────────────────────────────────────────
|
|
3525
|
-
<StyledSkeleton width="100%" height={14} animation="shimmer" speed={600} /> {/* fast */}
|
|
3526
|
-
<StyledSkeleton width="100%" height={14} animation="shimmer" speed={1400} /> {/* default */}
|
|
3527
|
-
<StyledSkeleton width="100%" height={14} animation="shimmer" speed={2200} /> {/* slow */}
|
|
3528
|
-
|
|
3529
|
-
// ── 6. Dark theme ─────────────────────────────────────────────────────────────
|
|
3530
|
-
<StyledSkeleton template="profile" animation="shimmer" skeletonTheme="dark" />
|
|
3531
|
-
|
|
3532
|
-
// ── 7. Colour overrides (indigo) ──────────────────────────────────────────────
|
|
3533
|
-
<StyledSkeleton width="100%" height={13} shape="text" animation="pulse"
|
|
3534
|
-
colors={{ base: '#e0e7ff', highlight: '#eef2ff', shimmer: 'rgba(99,102,241,0.15)' }} />
|
|
3535
|
-
|
|
3536
|
-
// ── 8. Custom layout with SkeletonBone ────────────────────────────────────────
|
|
3537
|
-
// SkeletonBone renders a single animated bone directly — useful for bespoke layouts.
|
|
3538
|
-
<Stack horizontal gap={14} alignItems="center" padding={16}>
|
|
3539
|
-
<SkeletonBone width={48} height={48} shape="circle" animation="shimmer" speed={1400}
|
|
3540
|
-
colors={{ base: '#fde68a', highlight: '#fef9c3', shimmer: 'rgba(245,158,11,0.25)' }} />
|
|
3541
|
-
<Stack flex={1} gap={8}>
|
|
3542
|
-
<SkeletonBone width="70%" height={14} shape="text" animation="shimmer" speed={1400}
|
|
3543
|
-
colors={{ base: '#fde68a', highlight: '#fef9c3', shimmer: 'rgba(245,158,11,0.25)' }} />
|
|
3544
|
-
<SkeletonBone width="45%" height={12} shape="text" animation="shimmer" speed={1400}
|
|
3545
|
-
colors={{ base: '#fde68a', highlight: '#fef9c3', shimmer: 'rgba(245,158,11,0.25)' }} />
|
|
3546
|
-
</Stack>
|
|
3547
|
-
</Stack>
|
|
3548
|
-
```
|
|
3549
|
-
|
|
3550
|
-
---
|
|
3551
|
-
|
|
3552
2680
|
## Hooks
|
|
3553
2681
|
|
|
3554
2682
|
All hooks require a `PortalManager` ancestor.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluent-styles",
|
|
3
3
|
"description": "Develop different styled versions of UI components.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.56.0",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"react-native": "src/index.ts",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"fluent-styles"
|
|
22
22
|
],
|
|
23
23
|
"license": "Apache-2.0",
|
|
24
|
-
"homepage": "https://github.com/suftnetrepo/fluent-styles#readme",
|
|
24
|
+
"homepage": "https://github.com/suftnetrepo/fluent-styles-next#readme",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/suftnetrepo/fluent-styles.git"
|
|
27
|
+
"url": "https://github.com/suftnetrepo/fluent-styles-next.git"
|
|
28
28
|
},
|
|
29
29
|
"bugs": {
|
|
30
|
-
"url": "https://github.com/suftnetrepo/fluent-styles/issues"
|
|
30
|
+
"url": "https://github.com/suftnetrepo/fluent-styles-next/issues"
|
|
31
31
|
},
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"registry": "https://registry.npmjs.org"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"lint-staged": "lint-staged --allow-empty",
|
|
43
43
|
"doc": "dumi dev",
|
|
44
44
|
"doc:build": "dumi build",
|
|
45
|
-
"doc:github-build": "cross-env PUBLIC_PATH=fluent-styles/ dumi build",
|
|
45
|
+
"doc:github-build": "cross-env PUBLIC_PATH=fluent-styles-next/ dumi build",
|
|
46
46
|
"prepare": "husky install",
|
|
47
47
|
"build": "bob build",
|
|
48
48
|
"watch": "bob build --watch",
|