@neko-os/ui 0.4.0 → 0.5.1

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.
Files changed (77) hide show
  1. package/dist/abstractions/KeyboardDismissView.js +3 -0
  2. package/dist/abstractions/KeyboardDismissView.native.js +9 -0
  3. package/dist/abstractions/index.js +1 -0
  4. package/dist/components/actions/ClearLink.js +6 -0
  5. package/dist/components/actions/FloatingMenu.js +1 -1
  6. package/dist/components/calendar/CalendarNav.js +6 -6
  7. package/dist/components/carousel/Carousel.js +48 -0
  8. package/dist/components/carousel/CarouselArrows.js +40 -0
  9. package/dist/components/carousel/CarouselArrows.native.js +40 -0
  10. package/dist/components/carousel/CarouselDots.js +32 -0
  11. package/dist/components/carousel/CarouselDots.native.js +36 -0
  12. package/dist/components/carousel/CarouselHandler.js +86 -0
  13. package/dist/components/carousel/CarouselSlider.js +124 -0
  14. package/dist/components/carousel/CarouselSlider.native.js +121 -0
  15. package/dist/components/carousel/InfiniteCarousel.js +50 -0
  16. package/dist/components/carousel/index.js +6 -0
  17. package/dist/components/form/Form.js +5 -3
  18. package/dist/components/index.js +3 -1
  19. package/dist/components/inputs/DateInput.js +7 -4
  20. package/dist/components/inputs/InputWrapper.js +1 -2
  21. package/dist/components/inputs/Select.js +56 -52
  22. package/dist/components/inputs/TextInput.js +7 -6
  23. package/dist/components/inputs/datePicker/DayPicker.js +71 -23
  24. package/dist/components/inputs/datePicker/MonthPicker.js +61 -32
  25. package/dist/components/inputs/datePicker/QuarterPicker.js +62 -33
  26. package/dist/components/inputs/datePicker/WeekPicker.js +65 -24
  27. package/dist/components/inputs/datePicker/YearPicker.js +69 -40
  28. package/dist/components/keyboard/KeyboardDismissButton.js +3 -0
  29. package/dist/components/keyboard/KeyboardDismissButton.native.js +38 -0
  30. package/dist/components/keyboard/index.js +1 -0
  31. package/dist/components/modals/bottomDrawer/native/BottomDrawer.js +28 -7
  32. package/dist/components/presentation/LabelValue.js +1 -1
  33. package/dist/components/presentation/Result.js +11 -3
  34. package/dist/components/structure/KeyboardAvoidingView.js +9 -2
  35. package/dist/components/theme/ThemePicker.js +7 -12
  36. package/dist/components/theme/ThemeThumb.js +1 -1
  37. package/dist/index.js +1 -0
  38. package/dist/theme/ThemeHandler.js +31 -3
  39. package/package.json +1 -1
  40. package/src/abstractions/KeyboardDismissView.js +3 -0
  41. package/src/abstractions/KeyboardDismissView.native.js +9 -0
  42. package/src/abstractions/index.js +1 -0
  43. package/src/components/actions/ClearLink.js +6 -0
  44. package/src/components/actions/FloatingMenu.js +1 -1
  45. package/src/components/calendar/CalendarNav.js +6 -6
  46. package/src/components/carousel/Carousel.js +48 -0
  47. package/src/components/carousel/CarouselArrows.js +40 -0
  48. package/src/components/carousel/CarouselArrows.native.js +40 -0
  49. package/src/components/carousel/CarouselDots.js +32 -0
  50. package/src/components/carousel/CarouselDots.native.js +36 -0
  51. package/src/components/carousel/CarouselHandler.js +86 -0
  52. package/src/components/carousel/CarouselSlider.js +124 -0
  53. package/src/components/carousel/CarouselSlider.native.js +121 -0
  54. package/src/components/carousel/InfiniteCarousel.js +50 -0
  55. package/src/components/carousel/index.js +6 -0
  56. package/src/components/form/Form.js +2 -0
  57. package/src/components/index.js +2 -0
  58. package/src/components/inputs/DateInput.js +4 -1
  59. package/src/components/inputs/InputWrapper.js +1 -2
  60. package/src/components/inputs/Select.js +19 -15
  61. package/src/components/inputs/TextInput.js +5 -4
  62. package/src/components/inputs/datePicker/DayPicker.js +69 -21
  63. package/src/components/inputs/datePicker/MonthPicker.js +60 -31
  64. package/src/components/inputs/datePicker/QuarterPicker.js +60 -31
  65. package/src/components/inputs/datePicker/WeekPicker.js +63 -22
  66. package/src/components/inputs/datePicker/YearPicker.js +68 -39
  67. package/src/components/keyboard/KeyboardDismissButton.js +3 -0
  68. package/src/components/keyboard/KeyboardDismissButton.native.js +38 -0
  69. package/src/components/keyboard/index.js +1 -0
  70. package/src/components/modals/bottomDrawer/native/BottomDrawer.js +27 -6
  71. package/src/components/presentation/LabelValue.js +1 -1
  72. package/src/components/presentation/Result.js +10 -2
  73. package/src/components/structure/KeyboardAvoidingView.js +9 -2
  74. package/src/components/theme/ThemePicker.js +8 -13
  75. package/src/components/theme/ThemeThumb.js +1 -1
  76. package/src/index.js +1 -0
  77. package/src/theme/ThemeHandler.js +31 -3
@@ -1,6 +1,7 @@
1
- var _jsxFileName = "/Users/christianstorch/Apps/nekoapps/libs/neko-ui/src/components/structure/KeyboardAvoidingView.js";var _excluded = ["children", "keyboardVerticalOffset"];function _slicedToArray(r, e) {return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();}function _nonIterableRest() {throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}function _unsupportedIterableToArray(r, a) {if (r) {if ("string" == typeof r) return _arrayLikeToArray(r, a);var t = {}.toString.call(r).slice(8, -1);return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;}}function _arrayLikeToArray(r, a) {(null == a || a > r.length) && (a = r.length);for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];return n;}function _iterableToArrayLimit(r, l) {var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];if (null != t) {var e,n,i,u,a = [],f = !0,o = !1;try {if (i = (t = t.call(r)).next, 0 === l) {if (Object(t) !== t) return;f = !1;} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);} catch (r) {o = !0, n = r;} finally {try {if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;} finally {if (o) throw n;}}return a;}}function _arrayWithHoles(r) {if (Array.isArray(r)) return r;}function _objectWithoutProperties(e, t) {if (null == e) return {};var o,r,i = _objectWithoutPropertiesLoose(e, t);if (Object.getOwnPropertySymbols) {var n = Object.getOwnPropertySymbols(e);for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);}return i;}function _objectWithoutPropertiesLoose(r, e) {if (null == r) return {};var t = {};for (var n in r) if ({}.hasOwnProperty.call(r, n)) {if (-1 !== e.indexOf(n)) continue;t[n] = r[n];}return t;}import { pipe } from 'ramda';
1
+ var _jsxFileName = "/Users/christianstorch/Apps/nekoapps/libs/neko-ui/src/components/structure/KeyboardAvoidingView.js";var _excluded = ["children", "keyboardVerticalOffset", "dismissOnTap"];function _slicedToArray(r, e) {return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();}function _nonIterableRest() {throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}function _unsupportedIterableToArray(r, a) {if (r) {if ("string" == typeof r) return _arrayLikeToArray(r, a);var t = {}.toString.call(r).slice(8, -1);return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;}}function _arrayLikeToArray(r, a) {(null == a || a > r.length) && (a = r.length);for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];return n;}function _iterableToArrayLimit(r, l) {var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];if (null != t) {var e,n,i,u,a = [],f = !0,o = !1;try {if (i = (t = t.call(r)).next, 0 === l) {if (Object(t) !== t) return;f = !1;} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);} catch (r) {o = !0, n = r;} finally {try {if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;} finally {if (o) throw n;}}return a;}}function _arrayWithHoles(r) {if (Array.isArray(r)) return r;}function _objectWithoutProperties(e, t) {if (null == e) return {};var o,r,i = _objectWithoutPropertiesLoose(e, t);if (Object.getOwnPropertySymbols) {var n = Object.getOwnPropertySymbols(e);for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);}return i;}function _objectWithoutPropertiesLoose(r, e) {if (null == r) return {};var t = {};for (var n in r) if ({}.hasOwnProperty.call(r, n)) {if (-1 !== e.indexOf(n)) continue;t[n] = r[n];}return t;}import { pipe } from 'ramda';
2
2
 
3
3
  import { AbsKeyboardAvoidingView } from "../../abstractions/KeyboardAvoidingView";
4
+ import { AbsKeyboardDismissView } from "../../abstractions/KeyboardDismissView";
4
5
  import { Platform } from "../../abstractions/Platform";
5
6
  import { useSafeAreaInsets } from "../../abstractions/helpers/useSafeAreaInsets";
6
7
  import { useAnimationModifier } from "../../modifiers/animation";
@@ -19,7 +20,7 @@ import { useSizeModifier } from "../../modifiers/size";
19
20
  import { useStateModifier } from "../../modifiers/state";
20
21
  import { useThemeComponentModifier } from "../../modifiers/themeComponent";import { jsx as _jsx } from "react/jsx-runtime";
21
22
 
22
- export function KeyboardAvoidingView(_ref) {var children = _ref.children,_ref$keyboardVertical = _ref.keyboardVerticalOffset,keyboardVerticalOffset = _ref$keyboardVertical === void 0 ? 0 : _ref$keyboardVertical,rootProps = _objectWithoutProperties(_ref, _excluded);
23
+ export function KeyboardAvoidingView(_ref) {var children = _ref.children,_ref$keyboardVertical = _ref.keyboardVerticalOffset,keyboardVerticalOffset = _ref$keyboardVertical === void 0 ? 0 : _ref$keyboardVertical,_ref$dismissOnTap = _ref.dismissOnTap,dismissOnTap = _ref$dismissOnTap === void 0 ? true : _ref$dismissOnTap,rootProps = _objectWithoutProperties(_ref, _excluded);
23
24
  var _useSafeAreaInsets = useSafeAreaInsets(),bottom = _useSafeAreaInsets.bottom;
24
25
 
25
26
  var _pipe = pipe(
@@ -46,7 +47,13 @@ export function KeyboardAvoidingView(_ref) {var children = _ref.children,_ref$ke
46
47
  keyboardVerticalOffset: keyboardVerticalOffset + bottom },
47
48
  props, { children:
48
49
 
50
+ dismissOnTap ?
51
+ _jsx(AbsKeyboardDismissView, { style: { flex: 1 }, children:
52
+ children }
53
+ ) :
54
+
49
55
  children })
56
+
50
57
  ));
51
58
 
52
59
  }
@@ -1,25 +1,20 @@
1
- var _jsxFileName = "/Users/christianstorch/Apps/nekoapps/libs/neko-ui/src/components/theme/ThemePicker.js";import { mapObjIndexed, mergeDeepRight, values, pipe, filter } from 'ramda';
1
+ var _jsxFileName = "/Users/christianstorch/Apps/nekoapps/libs/neko-ui/src/components/theme/ThemePicker.js";import { mapObjIndexed, values, pipe, reject, propEq } from 'ramda';
2
2
 
3
- import { DEFAULT_THEMES, useThemeHandler } from "../../theme";
3
+ import { useAllThemes, useThemeHandler } from "../../theme";
4
4
  import { IconLabel } from "../presentation";
5
5
  import { Link } from "../actions";
6
6
  import { Picker } from "../inputs";
7
7
  import { ThemeThumb } from "./ThemeThumb";import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
 
9
- export function ThemePicker(_ref) {var _this = this;var _onChange = _ref.onChange,onlyKeys = _ref.onlyKeys,hideKeys = _ref.hideKeys;
10
- var _useThemeHandler = useThemeHandler(),activeThemeKey = _useThemeHandler.activeThemeKey,themes = _useThemeHandler.themes,onChangeTheme = _useThemeHandler.onChangeTheme;
9
+ export function ThemePicker(_ref) {var _this = this;var _onChange = _ref.onChange;
10
+ var _useThemeHandler = useThemeHandler(),activeThemeKey = _useThemeHandler.activeThemeKey,onChangeTheme = _useThemeHandler.onChangeTheme;
11
+ var allThemes = useAllThemes();
11
12
 
12
13
  var options = pipe(
13
- mergeDeepRight(DEFAULT_THEMES),
14
14
  mapObjIndexed(function (obj, key) {return Object.assign({}, obj, { value: key, key: key });}),
15
15
  values,
16
- filter(function (item) {
17
- if (item.value === '_all') return false;
18
- if (onlyKeys && onlyKeys.includes(item.value)) return true;
19
- if (hideKeys && hideKeys.includes(item.value)) return false;
20
- return true;
21
- })
22
- )(themes);
16
+ reject(propEq('_all', 'value'))
17
+ )(allThemes);
23
18
 
24
19
  return (
25
20
  _jsx(Picker, {
@@ -4,7 +4,7 @@ import { useResponsiveValue } from "../../responsive";
4
4
  import { useThemeHandler } from "../../theme";import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
 
6
6
  export function ThemeThumb(_ref) {var value = _ref.value;
7
- var _useThemeHandler = useThemeHandler(),themes = _useThemeHandler.themes;
7
+ var _useThemeHandler = useThemeHandler(),themes = _useThemeHandler.rawThemesParam;
8
8
  var _useFormattedTheme = useFormattedTheme(themes, value),colors = _useFormattedTheme.colors,label = _useFormattedTheme.label;
9
9
  var isMobile = useResponsiveValue({ smd: true, df: false });
10
10
 
package/dist/index.js CHANGED
@@ -4,5 +4,6 @@ export * from "./theme";
4
4
  export * from "./responsive";
5
5
  export * from "./i18n";
6
6
  export * from "./NekoUI";
7
+ export * from "./abstractions";
7
8
 
8
9
  export var version = 41;
@@ -1,7 +1,8 @@
1
- var _jsxFileName = "/Users/christianstorch/Apps/nekoapps/libs/neko-ui/src/theme/ThemeHandler.js";function _slicedToArray(r, e) {return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();}function _nonIterableRest() {throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}function _unsupportedIterableToArray(r, a) {if (r) {if ("string" == typeof r) return _arrayLikeToArray(r, a);var t = {}.toString.call(r).slice(8, -1);return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;}}function _arrayLikeToArray(r, a) {(null == a || a > r.length) && (a = r.length);for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];return n;}function _iterableToArrayLimit(r, l) {var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];if (null != t) {var e,n,i,u,a = [],f = !0,o = !1;try {if (i = (t = t.call(r)).next, 0 === l) {if (Object(t) !== t) return;f = !1;} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);} catch (r) {o = !0, n = r;} finally {try {if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;} finally {if (o) throw n;}}return a;}}function _arrayWithHoles(r) {if (Array.isArray(r)) return r;}import { mergeDeepRight } from 'ramda';
1
+ var _jsxFileName = "/Users/christianstorch/Apps/nekoapps/libs/neko-ui/src/theme/ThemeHandler.js";function _slicedToArray(r, e) {return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();}function _nonIterableRest() {throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}function _unsupportedIterableToArray(r, a) {if (r) {if ("string" == typeof r) return _arrayLikeToArray(r, a);var t = {}.toString.call(r).slice(8, -1);return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;}}function _arrayLikeToArray(r, a) {(null == a || a > r.length) && (a = r.length);for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];return n;}function _iterableToArrayLimit(r, l) {var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];if (null != t) {var e,n,i,u,a = [],f = !0,o = !1;try {if (i = (t = t.call(r)).next, 0 === l) {if (Object(t) !== t) return;f = !1;} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);} catch (r) {o = !0, n = r;} finally {try {if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;} finally {if (o) throw n;}}return a;}}function _arrayWithHoles(r) {if (Array.isArray(r)) return r;}import { keys, mergeDeepRight, omit, pick, pickBy } from 'ramda';
2
2
  import React from 'react';
3
3
 
4
4
  import { DEFAULT_LIGHT_THEME } from "./default/lightTheme";
5
+ import { DEFAULT_THEMES } from "./default/themes";
5
6
  import { getThemeValue } from "./helpers/relatedScales";
6
7
  import { useFormattedTheme } from "./format/formatTheme";import { jsx as _jsx } from "react/jsx-runtime";
7
8
 
@@ -52,8 +53,33 @@ export function useMergeThemeComponent(name, props) {
52
53
  var themeProps = useThemeComponent(name);
53
54
  return mergeDeepRight(themeProps, props);
54
55
  }
56
+ export function useAllThemes() {
57
+ var _useThemeHandler2 = useThemeHandler(),disableDefaultThemes = _useThemeHandler2.disableDefaultThemes,enableOnlyThemes = _useThemeHandler2.enableOnlyThemes,rawThemesParam = _useThemeHandler2.rawThemesParam;
58
+ var themes = rawThemesParam || {};
59
+ var themesParamKeys = keys(themes);
60
+
61
+ var allThemes = mergeDeepRight(DEFAULT_THEMES, themes);
62
+ if (disableDefaultThemes === true) {
63
+ allThemes = pickBy(function (_, key) {return themesParamKeys.includes(key);}, allThemes);
64
+ } else if (disableDefaultThemes != null && disableDefaultThemes.length) {
65
+ allThemes = omit(disableDefaultThemes, allThemes);
66
+ }
67
+ if (enableOnlyThemes != null && enableOnlyThemes.length) {
68
+ allThemes = pick(enableOnlyThemes, allThemes);
69
+ }
70
+
71
+ return allThemes;
72
+ }
73
+
74
+ export function ThemeHandler(_ref)
75
+
76
+
77
+
78
+
79
+
80
+
55
81
 
56
- export function ThemeHandler(_ref) {var breakpoints = _ref.breakpoints,themes = _ref.themes,initTheme = _ref.initTheme,onChangeTheme = _ref.onChangeTheme,children = _ref.children;
82
+ {var breakpoints = _ref.breakpoints,themes = _ref.themes,initTheme = _ref.initTheme,onChangeTheme = _ref.onChangeTheme,children = _ref.children,disableDefaultThemes = _ref.disableDefaultThemes,enableOnlyThemes = _ref.enableOnlyThemes;
57
83
  var _React$useState = React.useState(false),_React$useState2 = _slicedToArray(_React$useState, 2),themePickerOpen = _React$useState2[0],setThemePickerOpen = _React$useState2[1];
58
84
  var openThemePicker = function openThemePicker() {return setThemePickerOpen(true);};
59
85
  var _React$useState3 = React.useState(initTheme || 'light'),_React$useState4 = _slicedToArray(_React$useState3, 2),activeThemeKey = _React$useState4[0],setActiveThemeKey = _React$useState4[1];
@@ -66,7 +92,9 @@ export function ThemeHandler(_ref) {var breakpoints = _ref.breakpoints,themes =
66
92
 
67
93
  var value = {
68
94
  theme: theme,
69
- themes: themes,
95
+ rawThemesParam: themes,
96
+ disableDefaultThemes: disableDefaultThemes,
97
+ enableOnlyThemes: enableOnlyThemes,
70
98
  activeThemeKey: activeThemeKey,
71
99
  toggleTheme: toggleTheme,
72
100
  themePickerOpen: themePickerOpen,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neko-os/ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "author": "Christian Storch <ccstorch@gmail.com>",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -0,0 +1,3 @@
1
+ export function AbsKeyboardDismissView({ children }) {
2
+ return children
3
+ }
@@ -0,0 +1,9 @@
1
+ import { Keyboard, Pressable } from 'react-native'
2
+
3
+ export function AbsKeyboardDismissView({ children, style }) {
4
+ return (
5
+ <Pressable onPress={Keyboard.dismiss} accessible={false} style={style}>
6
+ {children}
7
+ </Pressable>
8
+ )
9
+ }
@@ -0,0 +1 @@
1
+ export * from './Platform'
@@ -0,0 +1,6 @@
1
+ import { Link } from './Link'
2
+
3
+ export function ClearLink({ hide, value, onChange, ...props }) {
4
+ if (!!hide || !!value) return false
5
+ return <Link center red label="Remove Value" onPress={() => onChange(null)} padding="sm" {...props} />
6
+ }
@@ -14,7 +14,7 @@ export function FloatingMenu({ fixed, onChange, items, activeIndex, size = 'md',
14
14
 
15
15
  return (
16
16
  <View absolute={!fixed} fixed={fixed} left="md" right="md" centerH bottom={Math.max(insets.bottom / 2, 16)}>
17
- <WrapperView height={height} shadow round row paddingH="sm" bg={bg} {...props}>
17
+ <WrapperView height={height} shadow round row paddingH="sm" bg={bg} border="overlayDivider" {...props}>
18
18
  {items.map((item, index) => {
19
19
  const isActive = index === activeIndex
20
20
 
@@ -20,7 +20,7 @@ export function CalendarNav({ value, onChange, level, ...props }) {
20
20
 
21
21
  return (
22
22
  <View className="neko-calendar-nav" row centerV gap="xxs" height={30} {...props}>
23
- <Link onPress={prevDecade} aria-label="Previous decade" padding="xxs" paddingL={0}>
23
+ <Link onPress={prevDecade} aria-label="Previous decade" padding="sm" paddingL={0}>
24
24
  <Icon name="arrow-left-double-line" />
25
25
  </Link>
26
26
 
@@ -28,7 +28,7 @@ export function CalendarNav({ value, onChange, level, ...props }) {
28
28
  {year}-{year + 9}
29
29
  </Text>
30
30
 
31
- <Link onPress={nextDecade} aria-label="Next decade" padding="xxs" paddingR={0}>
31
+ <Link onPress={nextDecade} aria-label="Next decade" padding="sm" paddingR={0}>
32
32
  <Icon name="arrow-right-double-line" />
33
33
  </Link>
34
34
  </View>
@@ -37,12 +37,12 @@ export function CalendarNav({ value, onChange, level, ...props }) {
37
37
 
38
38
  return (
39
39
  <View className="neko-calendar-nav" row centerV gap="xxs" height={30} {...props}>
40
- <Link onPress={prevYear} aria-label="Previous year" padding="xxs" paddingL={0}>
40
+ <Link onPress={prevYear} aria-label="Previous year" padding="sm" paddingR={0}>
41
41
  <Icon name="arrow-left-double-line" />
42
42
  </Link>
43
43
 
44
44
  {showMonth && (
45
- <Link onPress={prevMonth} aria-label="Previous month" padding="xxs">
45
+ <Link onPress={prevMonth} aria-label="Previous month" padding="sm">
46
46
  <Icon name="arrow-left-s-line" />
47
47
  </Link>
48
48
  )}
@@ -54,12 +54,12 @@ export function CalendarNav({ value, onChange, level, ...props }) {
54
54
  </View>
55
55
 
56
56
  {showMonth && (
57
- <Link onPress={nextMonth} aria-label="Next month" padding="xxs">
57
+ <Link onPress={nextMonth} aria-label="Next month" padding="sm">
58
58
  <Icon name="arrow-right-s-line" />
59
59
  </Link>
60
60
  )}
61
61
 
62
- <Link onPress={nextYear} aria-label="Next year" padding="xxs" paddingR={0}>
62
+ <Link onPress={nextYear} aria-label="Next year" padding="sm" paddingL={0}>
63
63
  <Icon name="arrow-right-double-line" />
64
64
  </Link>
65
65
  </View>
@@ -0,0 +1,48 @@
1
+ import { View } from '../structure/View'
2
+ import { CarouselArrows } from './CarouselArrows'
3
+ import { CarouselDots } from './CarouselDots'
4
+ import { CarouselHandler } from './CarouselHandler'
5
+ import { CarouselSlider } from './CarouselSlider'
6
+
7
+ export function Carousel({
8
+ items,
9
+ activeIndex,
10
+ activeKey,
11
+ onChange,
12
+ afterChange,
13
+ autoplay,
14
+ autoplaySpeed,
15
+ draggable,
16
+ loop,
17
+ showDots,
18
+ showArrows,
19
+ floatingDots,
20
+ dotsProps,
21
+ arrowsProps,
22
+ children,
23
+ ...rootProps
24
+ }) {
25
+ return (
26
+ <CarouselHandler
27
+ items={items}
28
+ activeIndex={activeIndex}
29
+ activeKey={activeKey}
30
+ onChange={onChange}
31
+ afterChange={afterChange}
32
+ autoplay={autoplay}
33
+ autoplaySpeed={autoplaySpeed}
34
+ draggable={draggable}
35
+ loop={loop}
36
+ >
37
+ <View {...rootProps}>
38
+ <View relative>
39
+ <CarouselSlider />
40
+ {showArrows && <CarouselArrows {...arrowsProps} />}
41
+ {showDots && floatingDots && <CarouselDots absolute bottom="xs" left={0} right={0} {...dotsProps} />}
42
+ </View>
43
+ {showDots && !floatingDots && <CarouselDots {...dotsProps} />}
44
+ {children}
45
+ </View>
46
+ </CarouselHandler>
47
+ )
48
+ }
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+
3
+ import { Icon } from '../presentation/Icon'
4
+ import { Link } from '../actions/Link'
5
+ import { View } from '../structure/View'
6
+ import { useCarousel } from './CarouselHandler'
7
+
8
+ export function CarouselArrows({ iconSize = 'md', ...props }) {
9
+ const { goToNext, goToPrev, activeIndex, itemsCount, loop } = useCarousel()
10
+
11
+ const showPrev = loop || activeIndex > 0
12
+ const showNext = loop || activeIndex < itemsCount - 1
13
+
14
+ return (
15
+ <View
16
+ absoluteFill
17
+ row
18
+ centerV
19
+ paddingH="xs"
20
+ justify="space-between"
21
+ style={{ pointerEvents: 'none' }}
22
+ {...props}
23
+ >
24
+ {showPrev ? (
25
+ <Link onPress={goToPrev} bg="overlayBG_op80" round padding="xs" shadow style={{ pointerEvents: 'auto' }}>
26
+ <Icon name="arrow-left-s-line" text3 size={iconSize} />
27
+ </Link>
28
+ ) : (
29
+ <View />
30
+ )}
31
+ {showNext ? (
32
+ <Link onPress={goToNext} bg="overlayBG_op80" round padding="xs" shadow style={{ pointerEvents: 'auto' }}>
33
+ <Icon name="arrow-right-s-line" text3 size={iconSize} />
34
+ </Link>
35
+ ) : (
36
+ <View />
37
+ )}
38
+ </View>
39
+ )
40
+ }
@@ -0,0 +1,40 @@
1
+ import React from 'react'
2
+
3
+ import { Icon } from '../presentation/Icon'
4
+ import { Link } from '../actions/Link'
5
+ import { View } from '../structure/View'
6
+ import { useCarousel } from './CarouselHandler'
7
+
8
+ export function CarouselArrows({ iconSize = 'md', ...props }) {
9
+ const { goToNext, goToPrev, activeIndex, itemsCount, loop } = useCarousel()
10
+
11
+ const showPrev = loop || activeIndex > 0
12
+ const showNext = loop || activeIndex < itemsCount - 1
13
+
14
+ return (
15
+ <View
16
+ absoluteFill
17
+ row
18
+ centerV
19
+ paddingH="xs"
20
+ justify="space-between"
21
+ pointerEvents="box-none"
22
+ {...props}
23
+ >
24
+ {showPrev ? (
25
+ <Link onPress={goToPrev} bg="overlayBG_op80" round padding="xs" shadow>
26
+ <Icon name="arrow-left-s-line" text3 size={iconSize} />
27
+ </Link>
28
+ ) : (
29
+ <View />
30
+ )}
31
+ {showNext ? (
32
+ <Link onPress={goToNext} bg="overlayBG_op80" round padding="xs" shadow>
33
+ <Icon name="arrow-right-s-line" text3 size={iconSize} />
34
+ </Link>
35
+ ) : (
36
+ <View />
37
+ )}
38
+ </View>
39
+ )
40
+ }
@@ -0,0 +1,32 @@
1
+ import React from 'react'
2
+
3
+ import { Pressable } from '../actions/Pressable'
4
+ import { View } from '../structure/View'
5
+ import { useCarousel } from './CarouselHandler'
6
+
7
+ function Dot({ active, onPress }) {
8
+ return (
9
+ <Pressable
10
+ onPress={onPress}
11
+ width={active ? 20 : 8}
12
+ height={8}
13
+ round
14
+ bg={active ? 'primary' : 'text4_op30'}
15
+ style={{ transition: 'all 200ms ease-in-out' }}
16
+ />
17
+ )
18
+ }
19
+
20
+ export function CarouselDots(props) {
21
+ const { items, activeIndex, goTo } = useCarousel()
22
+
23
+ if (!items?.length) return null
24
+
25
+ return (
26
+ <View row center gap="xs" paddingV="sm" {...props}>
27
+ {items.map((item, index) => (
28
+ <Dot key={item.key} active={index === activeIndex} onPress={() => goTo(index)} />
29
+ ))}
30
+ </View>
31
+ )
32
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react'
2
+ import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
3
+
4
+ import { Pressable } from '../actions/Pressable'
5
+ import { View } from '../structure/View'
6
+ import { useColors } from '../../theme/ThemeHandler'
7
+ import { useCarousel } from './CarouselHandler'
8
+
9
+ function Dot({ active, onPress }) {
10
+ const colors = useColors()
11
+
12
+ const animatedStyle = useAnimatedStyle(() => ({
13
+ width: withTiming(active ? 20 : 8, { duration: 200 }),
14
+ backgroundColor: withTiming(active ? colors.primary : colors.text4_op30, { duration: 200 }),
15
+ }), [active])
16
+
17
+ return (
18
+ <Pressable onPress={onPress}>
19
+ <Animated.View style={[{ height: 8, borderRadius: 4 }, animatedStyle]} />
20
+ </Pressable>
21
+ )
22
+ }
23
+
24
+ export function CarouselDots(props) {
25
+ const { items, activeIndex, goTo } = useCarousel()
26
+
27
+ if (!items?.length) return null
28
+
29
+ return (
30
+ <View row center gap="xs" paddingV="sm" {...props}>
31
+ {items.map((item, index) => (
32
+ <Dot key={item.key} active={index === activeIndex} onPress={() => goTo(index)} />
33
+ ))}
34
+ </View>
35
+ )
36
+ }
@@ -0,0 +1,86 @@
1
+ import React from 'react'
2
+
3
+ const CarouselContext = React.createContext(null)
4
+ export const useCarousel = () => React.useContext(CarouselContext) || {}
5
+
6
+ export function CarouselHandler({
7
+ children,
8
+ items,
9
+ activeIndex: controlledIndex,
10
+ activeKey,
11
+ onChange,
12
+ afterChange,
13
+ autoplay,
14
+ autoplaySpeed = 3000,
15
+ draggable,
16
+ loop,
17
+ }) {
18
+ const itemsCount = items?.length || 0
19
+ const isControlled = controlledIndex !== undefined || activeKey !== undefined
20
+
21
+ const [internalIndex, setInternalIndex] = React.useState(() => {
22
+ if (controlledIndex !== undefined) return controlledIndex
23
+ if (activeKey !== undefined) return Math.max(0, items?.findIndex((i) => i.key === activeKey) || 0)
24
+ return 0
25
+ })
26
+
27
+ const resolvedIndex = isControlled
28
+ ? activeKey !== undefined
29
+ ? Math.max(0, items?.findIndex((i) => i.key === activeKey) || 0)
30
+ : controlledIndex
31
+ : internalIndex
32
+
33
+ const activeItem = React.useMemo(() => items?.[resolvedIndex], [items, resolvedIndex])
34
+
35
+ const pausedRef = React.useRef(false)
36
+
37
+ const goTo = React.useCallback(
38
+ (index) => {
39
+ let next = index
40
+ if (loop) next = ((next % itemsCount) + itemsCount) % itemsCount
41
+ else next = Math.max(0, Math.min(next, itemsCount - 1))
42
+
43
+ if (!isControlled) setInternalIndex(next)
44
+ onChange?.(items?.[next]?.key, next)
45
+ },
46
+ [loop, itemsCount, isControlled, onChange, items]
47
+ )
48
+
49
+ const goToNext = React.useCallback(() => goTo(resolvedIndex + 1), [goTo, resolvedIndex])
50
+ const goToPrev = React.useCallback(() => goTo(resolvedIndex - 1), [goTo, resolvedIndex])
51
+
52
+ const pauseAutoplay = React.useCallback(() => {
53
+ pausedRef.current = true
54
+ }, [])
55
+
56
+ const resumeAutoplay = React.useCallback(() => {
57
+ pausedRef.current = false
58
+ }, [])
59
+
60
+ React.useEffect(() => {
61
+ if (!autoplay || itemsCount <= 1) return
62
+
63
+ const interval = setInterval(() => {
64
+ if (!pausedRef.current) goToNext()
65
+ }, autoplaySpeed)
66
+
67
+ return () => clearInterval(interval)
68
+ }, [autoplay, autoplaySpeed, itemsCount, resolvedIndex, goToNext])
69
+
70
+ const value = {
71
+ items,
72
+ activeIndex: resolvedIndex,
73
+ activeItem,
74
+ itemsCount,
75
+ goTo,
76
+ goToNext,
77
+ goToPrev,
78
+ afterChange,
79
+ draggable,
80
+ loop,
81
+ pauseAutoplay,
82
+ resumeAutoplay,
83
+ }
84
+
85
+ return <CarouselContext.Provider value={value}>{children}</CarouselContext.Provider>
86
+ }
@@ -0,0 +1,124 @@
1
+ import React from 'react'
2
+
3
+ import { View } from '../structure/View'
4
+ import { useCarousel } from './CarouselHandler'
5
+
6
+ function SlideContent({ item }) {
7
+ const Content = React.useMemo(
8
+ () => item.render || item.renderContent || item.Content,
9
+ [item.render, item.renderContent, item.Content]
10
+ )
11
+ return Content ? <Content /> : null
12
+ }
13
+
14
+ export function CarouselSlider() {
15
+ const { items, activeIndex, itemsCount, draggable, loop, afterChange, goToNext, goToPrev, pauseAutoplay, resumeAutoplay } =
16
+ useCarousel()
17
+
18
+ const containerRef = React.useRef(null)
19
+ const [isDragging, setIsDragging] = React.useState(false)
20
+ const [dragOffset, setDragOffset] = React.useState(0)
21
+ const startXRef = React.useRef(0)
22
+ const startTimeRef = React.useRef(0)
23
+ const prevItemsRef = React.useRef(items)
24
+ const skipTransitionRef = React.useRef(false)
25
+
26
+ if (items !== prevItemsRef.current) {
27
+ skipTransitionRef.current = true
28
+ prevItemsRef.current = items
29
+ }
30
+
31
+ React.useEffect(() => {
32
+ if (skipTransitionRef.current) {
33
+ const id = requestAnimationFrame(() => {
34
+ skipTransitionRef.current = false
35
+ })
36
+ return () => cancelAnimationFrame(id)
37
+ }
38
+ })
39
+
40
+ if (!items?.length) return null
41
+
42
+ const baseTranslate = -(activeIndex * 100) / itemsCount
43
+ const dragPercent =
44
+ isDragging && containerRef.current ? (dragOffset / containerRef.current.offsetWidth) * (100 / itemsCount) : 0
45
+ const transformX = baseTranslate + dragPercent
46
+
47
+ const dragStateRef = React.useRef({ isDragging: false, dragOffset: 0 })
48
+
49
+ const handlePointerDown = (e) => {
50
+ if (!draggable) return
51
+ startXRef.current = e.clientX
52
+ startTimeRef.current = Date.now()
53
+ dragStateRef.current = { isDragging: true, dragOffset: 0 }
54
+ setIsDragging(true)
55
+ setDragOffset(0)
56
+ pauseAutoplay()
57
+
58
+ const onMove = (ev) => {
59
+ const raw = ev.clientX - startXRef.current
60
+ let offset = raw
61
+ if (!loop) {
62
+ const containerWidth = containerRef.current?.offsetWidth || 1
63
+ const atStart = activeIndex === 0 && raw > 0
64
+ const atEnd = activeIndex === itemsCount - 1 && raw < 0
65
+ if (atStart || atEnd) offset = raw * 0.3
66
+ }
67
+ dragStateRef.current.dragOffset = offset
68
+ setDragOffset(offset)
69
+ }
70
+
71
+ const onUp = () => {
72
+ window.removeEventListener('pointermove', onMove)
73
+ window.removeEventListener('pointerup', onUp)
74
+ window.removeEventListener('pointercancel', onUp)
75
+
76
+ const offset = dragStateRef.current.dragOffset
77
+ const containerWidth = containerRef.current?.offsetWidth || 1
78
+ const threshold = containerWidth * 0.25
79
+ const elapsed = Date.now() - startTimeRef.current
80
+ const velocity = Math.abs(offset) / (elapsed || 1)
81
+
82
+ if (offset < -threshold || (offset < 0 && velocity > 0.5)) {
83
+ goToNext()
84
+ } else if (offset > threshold || (offset > 0 && velocity > 0.5)) {
85
+ goToPrev()
86
+ }
87
+
88
+ dragStateRef.current = { isDragging: false, dragOffset: 0 }
89
+ setIsDragging(false)
90
+ setDragOffset(0)
91
+ resumeAutoplay()
92
+ }
93
+
94
+ window.addEventListener('pointermove', onMove)
95
+ window.addEventListener('pointerup', onUp)
96
+ window.addEventListener('pointercancel', onUp)
97
+ }
98
+
99
+ return (
100
+ <View hiddenOverflow fullW ref={containerRef}>
101
+ <View
102
+ row
103
+ style={{
104
+ width: `${itemsCount * 100}%`,
105
+ transform: `translateX(${transformX}%)`,
106
+ transition: isDragging || skipTransitionRef.current ? 'none' : 'transform 300ms ease-in-out',
107
+ touchAction: 'pan-y',
108
+ cursor: draggable ? (isDragging ? 'grabbing' : 'grab') : undefined,
109
+ userSelect: 'none',
110
+ }}
111
+ onTransitionEnd={afterChange ? (e) => {
112
+ if (e.propertyName === 'transform') afterChange(items?.[activeIndex]?.key, activeIndex)
113
+ } : undefined}
114
+ onPointerDown={draggable ? handlePointerDown : undefined}
115
+ >
116
+ {items.map((item) => (
117
+ <View key={item.key} style={{ width: `${100 / itemsCount}%` }}>
118
+ <SlideContent item={item} />
119
+ </View>
120
+ ))}
121
+ </View>
122
+ </View>
123
+ )
124
+ }