diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index ddd7493..db90fbb 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -5,6 +5,7 @@ on:
push:
branches:
- main
+
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@@ -67,11 +68,6 @@ jobs:
- name: Setup Pages
uses: actions/configure-pages@v5
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v3
- with:
- path: workspaces/docs/dist
-
- name: Deploy artifact
id: deployment
uses: actions/deploy-pages@v5
diff --git a/package-lock.json b/package-lock.json
index a783b28..fd2f954 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17432,7 +17432,7 @@
"@react-native/babel-preset": "^0.80.2",
"@types/jest": "^30.0.0",
"@types/lodash.uniqueid": "^4.0.8",
- "@types/react": "^19.1.11",
+ "@types/react": "~19.0.10",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^9.34.0",
@@ -17453,16 +17453,6 @@
"react-native-safe-area-context": ">=5.0.0"
}
},
- "workspaces/package/node_modules/@types/react": {
- "version": "19.2.15",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
- "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "csstype": "^3.2.2"
- }
- },
"workspaces/package/node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
diff --git a/package.json b/package.json
index 5c66047..16f8f8e 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"license": "MIT",
"readme": "./README.md",
"author": "ImRoodyDev <> (https://github.com/imroodydev)",
- "homepage": "https://github.com/imroodydev/react-native-cross-elements#readme",
+ "homepage": "https://imroodydev.github.io/react-native-cross-elements/",
"repository": {
"type": "git",
"url": "https://github.com/imroodydev/react-native-cross-elements.git"
@@ -21,6 +21,7 @@
},
"scripts": {
"start": "npm run start -w docs",
+ "build:pkg:local": "npm run build:local -w react-native-cross-elements",
"build:pkg": "npm run build -w react-native-cross-elements"
}
}
diff --git a/workspaces/docs/app.json b/workspaces/docs/app.json
index e34142a..16d769d 100644
--- a/workspaces/docs/app.json
+++ b/workspaces/docs/app.json
@@ -5,8 +5,17 @@
"version": "1.0.0",
"scheme": "docs",
"userInterfaceStyle": "automatic",
-
+ "newArchEnabled": true,
"platforms": ["ios", "android", "web"],
+ "ios": {
+ "supportsTablet": true
+ },
+ "android": {
+ "adaptiveIcon": {
+ "backgroundColor": "#E6F4FE"
+ },
+ "edgeToEdgeEnabled": true
+ },
"web": {
"bundler": "metro",
"output": "static"
@@ -14,7 +23,8 @@
"plugins": ["expo-router"],
"experiments": {
"typedRoutes": true,
- "baseUrl": "/react-native-cross-elements"
+ "baseUrl": "/react-native-cross-elements",
+ "autolinkingModuleResolution": true
},
"backgroundColor": "#09090b"
}
diff --git a/workspaces/docs/app/(docs)/components/auto-detect-buttons-slider.tsx b/workspaces/docs/app/(docs)/components/auto-detect-buttons-slider.tsx
index ef72565..a12b383 100644
--- a/workspaces/docs/app/(docs)/components/auto-detect-buttons-slider.tsx
+++ b/workspaces/docs/app/(docs)/components/auto-detect-buttons-slider.tsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
-import { View, Text, Pressable } from 'react-native';
+import { View, Text } from 'react-native';
+import { AutoDetectButtonsSlider } from 'react-native-cross-elements';
import { DocPage, Callout } from '../../../components/DocPage';
import { CodeBlock } from '../../../components/CodeBlock';
import { ComponentPreview } from '../../../components/ComponentPreview';
@@ -76,30 +77,24 @@ function AutoSliderDemo() {
const options = ['One', 'Two', 'Three', 'Four'];
return (
-
- {options.map((opt, i) => (
- setSelected(i)}
- style={{
- paddingHorizontal: 14,
- paddingVertical: 8,
- borderRadius: 99,
- backgroundColor: i === selected ? '#6366f1' : 'transparent',
- }}
- >
-
- {opt}
-
-
- ))}
-
+ ({ backgroundColor: 'transparent' })}
+ sliderItemTextStyle={({ isSelected }) => ({
+ color: isSelected ? '#ffffff' : '#71717a',
+ fontWeight: isSelected ? '600' : '400',
+ fontSize: 14,
+ })}
+ style={{ width: 320, height: 44 }}
+ />
Selected: {options[selected]}
);
diff --git a/workspaces/docs/components/Navbar.tsx b/workspaces/docs/components/Navbar.tsx
index c116fa4..b3ca528 100644
--- a/workspaces/docs/components/Navbar.tsx
+++ b/workspaces/docs/components/Navbar.tsx
@@ -50,7 +50,6 @@ export function Navbar({ onMenuPress }: { onMenuPress?: () => void }) {
{/* Right — Search + GitHub + optional hamburger */}
- {isWide && }
Platform.OS === 'web'
diff --git a/workspaces/docs/global.css b/workspaces/docs/global.css
index 77092a8..24c69bb 100644
--- a/workspaces/docs/global.css
+++ b/workspaces/docs/global.css
@@ -1,62 +1,6 @@
+@import 'styles/app.css';
+
@tailwind base;
@tailwind components;
@tailwind utilities;
-
-@layer base {
- html,
- body {
- background-color: #09090b;
- color: #fafafa;
- min-height: 100%;
- margin: 0;
- padding: 0;
- overflow-x: hidden;
- overflow-y: auto;
- scrollbar-color: #3f3f46 #09090b;
- scrollbar-width: thin;
- -webkit-font-smoothing: antialiased;
- }
-
- #root {
- min-height: 100%;
- display: flex;
- flex-direction: column;
- }
-
- ::-webkit-scrollbar {
- width: 12px;
- height: 12px;
- }
-
- ::-webkit-scrollbar-track {
- background: #09090b;
- }
-
- ::-webkit-scrollbar-thumb {
- background: #3f3f46;
- border-radius: 999px;
- border: 3px solid #09090b;
- }
-
- ::-webkit-scrollbar-thumb:hover {
- background: #52525b;
- }
-
- .docs-flat-input {
- appearance: none;
- -webkit-appearance: none;
- background: transparent !important;
- border-radius: 0 !important;
- box-shadow: none !important;
- color: #e4e4e7;
- outline: none !important;
- }
-
- .docs-flat-input:-webkit-autofill,
- .docs-flat-input:-webkit-autofill:hover,
- .docs-flat-input:-webkit-autofill:focus {
- -webkit-text-fill-color: #e4e4e7;
- box-shadow: 0 0 0 1000px transparent inset !important;
- transition: background-color 9999s ease-out;
- }
-}
+@tailwind variants;
diff --git a/workspaces/docs/metro.config.js b/workspaces/docs/metro.config.js
index 023dcaf..f557a13 100644
--- a/workspaces/docs/metro.config.js
+++ b/workspaces/docs/metro.config.js
@@ -2,51 +2,66 @@ const { getDefaultConfig } = require('expo/metro-config');
const { withNativeWind } = require('nativewind/metro');
const path = require('path');
-const projectRoot = __dirname;
-const monorepoRoot = path.resolve(projectRoot, '../..');
-
-const config = getDefaultConfig(projectRoot);
-
-// Monorepo support: watch the whole repo and resolve node_modules from both
-config.watchFolders = [monorepoRoot];
-config.resolver.nodeModulesPaths = [
- path.resolve(projectRoot, 'node_modules'),
- path.resolve(monorepoRoot, 'node_modules'),
-];
-
-const defaultResolveRequest = config.resolver.resolveRequest;
-config.resolver.resolveRequest = (context, moduleName, platform) => {
- if (moduleName === 'react') {
- return {
- type: 'sourceFile',
- filePath: path.resolve(monorepoRoot, 'node_modules/react/index.js'),
- };
- }
-
- if (moduleName === 'react/jsx-runtime') {
- return {
- type: 'sourceFile',
- filePath: path.resolve(monorepoRoot, 'node_modules/react/jsx-runtime.js'),
- };
- }
-
- if (moduleName === 'react/jsx-dev-runtime') {
- return {
- type: 'sourceFile',
- filePath: path.resolve(monorepoRoot, 'node_modules/react/jsx-dev-runtime.js'),
- };
- }
-
- if (moduleName === 'pretty-format') {
- return {
- type: 'sourceFile',
- filePath: path.resolve(monorepoRoot, 'node_modules/pretty-format/build/index.js'),
- };
- }
-
- return defaultResolveRequest
- ? defaultResolveRequest(context, moduleName, platform)
- : context.resolveRequest(context, moduleName, platform);
-};
-
-module.exports = withNativeWind(config, { input: './global.css' });
+module.exports = (() => {
+ const projectRoot = __dirname;
+ const monorepoRoot = path.resolve(projectRoot, '../..');
+
+ const config = getDefaultConfig(__dirname);
+ const { transformer, resolver } = config;
+
+ // Monorepo support: watch the whole repo and resolve node_modules from both
+ config.watchFolders = [monorepoRoot];
+ config.resolver.nodeModulesPaths = [
+ path.resolve(projectRoot, 'node_modules'),
+ path.resolve(monorepoRoot, 'node_modules'),
+ ];
+
+ const defaultResolveRequest = config.resolver.resolveRequest;
+ config.resolver.resolveRequest = (context, moduleName, platform) => {
+ if (moduleName === 'react') {
+ return {
+ type: 'sourceFile',
+ filePath: path.resolve(monorepoRoot, 'node_modules/react/index.js'),
+ };
+ }
+
+ if (moduleName === 'react/jsx-runtime') {
+ return {
+ type: 'sourceFile',
+ filePath: path.resolve(monorepoRoot, 'node_modules/react/jsx-runtime.js'),
+ };
+ }
+
+ if (moduleName === 'react/jsx-dev-runtime') {
+ return {
+ type: 'sourceFile',
+ filePath: path.resolve(monorepoRoot, 'node_modules/react/jsx-dev-runtime.js'),
+ };
+ }
+
+ if (moduleName === 'pretty-format') {
+ return {
+ type: 'sourceFile',
+ filePath: path.resolve(monorepoRoot, 'node_modules/pretty-format/build/index.js'),
+ };
+ }
+
+ return defaultResolveRequest
+ ? defaultResolveRequest(context, moduleName, platform)
+ : context.resolveRequest(context, moduleName, platform);
+ };
+
+ config.transformer = {
+ ...transformer,
+ };
+ config.resolver = {
+ ...resolver,
+ assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'),
+ sourceExts: [...resolver.sourceExts, 'svg'],
+ extraNodeModules: {
+ ...(resolver.extraNodeModules || {}),
+ // crypto: require.resolve('react-native-quick-crypto'),
+ },
+ };
+ return withNativeWind(config, { input: './src/styles/global.css' });
+})();
diff --git a/workspaces/docs/package.json b/workspaces/docs/package.json
index bd42be6..3d01c13 100644
--- a/workspaces/docs/package.json
+++ b/workspaces/docs/package.json
@@ -5,7 +5,7 @@
"main": "expo-router/entry",
"scripts": {
"doctor": "npx expo-doctor",
- "start": "expo start",
+ "start": "expo start -c",
"web": "expo start --web",
"build:web": "expo export --platform web",
"typecheck": "tsc --noEmit"
diff --git a/workspaces/docs/styles/app.css b/workspaces/docs/styles/app.css
new file mode 100644
index 0000000..4908a0a
--- /dev/null
+++ b/workspaces/docs/styles/app.css
@@ -0,0 +1,56 @@
+html,
+body {
+ background-color: #09090b;
+ color: #fafafa;
+ min-height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ scrollbar-color: #3f3f46 #09090b;
+ scrollbar-width: thin;
+ -webkit-font-smoothing: antialiased;
+}
+
+#root {
+ min-height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+::-webkit-scrollbar {
+ width: 12px;
+ height: 12px;
+}
+
+::-webkit-scrollbar-track {
+ background: #09090b;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #3f3f46;
+ border-radius: 999px;
+ border: 3px solid #09090b;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #52525b;
+}
+
+.docs-flat-input {
+ appearance: none;
+ -webkit-appearance: none;
+ background: transparent !important;
+ border-radius: 0 !important;
+ box-shadow: none !important;
+ color: #e4e4e7;
+ outline: none !important;
+}
+
+.docs-flat-input:-webkit-autofill,
+.docs-flat-input:-webkit-autofill:hover,
+.docs-flat-input:-webkit-autofill:focus {
+ -webkit-text-fill-color: #e4e4e7;
+ box-shadow: 0 0 0 1000px transparent inset !important;
+ transition: background-color 9999s ease-out;
+}
diff --git a/workspaces/package/package.json b/workspaces/package/package.json
index 297597a..38b58d2 100644
--- a/workspaces/package/package.json
+++ b/workspaces/package/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-cross-elements",
- "version": "1.0.0_nightly.0",
+ "version": "1.0.0",
"description": "Beautiful, Web, Native and TV friendly interactable components and spatial navigation for React Native (iOS, Android, Web, TV) with accessibility for voice and screen reader support.",
"source": "src/index",
"react-native": "src/index.ts",
@@ -12,13 +12,14 @@
"src/"
],
"scripts": {
- "test:ts": "tsc --noEmit",
"clean": "rimraf dist",
"test": "jest",
"lint": "eslint ./",
"lint:fix": "eslint ./ --fix",
- "build": "npm run clean && bob build && npm pack",
- "publish": "npm publish --access public"
+ "build": "npm run clean && bob build",
+ "build:local": "npm run clean && bob build && npm pack",
+ "publish": "npm publish --access public",
+ "typecheck": "tsc --noEmit"
},
"keywords": [
"tv",
@@ -44,7 +45,7 @@
"@react-native/babel-preset": "^0.80.2",
"@types/jest": "^30.0.0",
"@types/lodash.uniqueid": "^4.0.8",
- "@types/react": "^19.1.11",
+ "@types/react": "~19.0.10",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^9.34.0",
@@ -96,7 +97,7 @@
[
"typescript",
{
- "project": "tsconfig.build.json"
+ "project": "tsconfig.json"
}
]
]
diff --git a/workspaces/package/src/interactables/components/Button/AutoDetectButtonsSlider.tsx b/workspaces/package/src/interactables/components/Button/AutoDetectButtonsSlider.tsx
index 326db9b..0e70b97 100644
--- a/workspaces/package/src/interactables/components/Button/AutoDetectButtonsSlider.tsx
+++ b/workspaces/package/src/interactables/components/Button/AutoDetectButtonsSlider.tsx
@@ -55,7 +55,9 @@ const InnerAutoDetectButtonsSlider = React.forwardRef((props: ButtonSliderProps,
const isHorizontal = currentOrientation === 'horizontal';
// Calculate button dimensions based on orientation
- const buttonSize = 100 / options.length;
+ const optionCount = Math.max(options.length, 1);
+ const buttonWidth = containerWidth / optionCount;
+ const buttonHeight = containerHeight / optionCount;
// Update the detected orientation when container dimensions change
useEffect(() => {
@@ -86,20 +88,26 @@ const InnerAutoDetectButtonsSlider = React.forwardRef((props: ButtonSliderProps,
const sliderAnimatedStyle = useAnimatedStyle(() => {
if (isHorizontal) {
return {
- left: `${(100 / options.length) * sliderPosition.value}%`,
- width: `${buttonSize}%`,
+ left: 0,
+ right: undefined,
+ width: buttonWidth,
+ height: undefined,
top: 0,
bottom: 0,
+ transform: [{translateX: buttonWidth * sliderPosition.value}],
};
} else {
return {
- top: `${(100 / options.length) * sliderPosition.value}%`,
- height: `${buttonSize}%`,
+ top: 0,
+ bottom: undefined,
+ height: buttonHeight,
+ width: undefined,
left: 0,
- right: 0
+ right: 0,
+ transform: [{translateY: buttonHeight * sliderPosition.value}],
};
}
- });
+ }, [isHorizontal, buttonWidth, buttonHeight]);
// Handle layout measurement to get container dimensions
const onLayout = (event: LayoutChangeEvent) => {
diff --git a/workspaces/package/src/interactables/components/Dropdown/SelectDropdown.tsx b/workspaces/package/src/interactables/components/Dropdown/SelectDropdown.tsx
index eb3978e..6ddfd74 100644
--- a/workspaces/package/src/interactables/components/Dropdown/SelectDropdown.tsx
+++ b/workspaces/package/src/interactables/components/Dropdown/SelectDropdown.tsx
@@ -1,4 +1,13 @@
-import React, { ComponentRef, Ref, useCallback, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
+import React, {
+ ComponentRef,
+ Ref,
+ useCallback,
+ useImperativeHandle,
+ useLayoutEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
import { FlatList, ListRenderItemInfo, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { isExist } from '../../../utils/isExist';
import Input from './Input';
@@ -128,12 +137,15 @@ export const Dropdown = typedForwardRef((props: DropdownProps, ref?: Ref<
/**
* Close dropdown and reset search
*/
- const closeDropdown = useCallback((onAfterClose?: () => void) => {
- setDropdownVisible(false, onAfterClose);
- onDropdownWillShow?.(false);
- setSearchTxt('');
- onBlur?.();
- }, [onDropdownWillShow, setSearchTxt, onBlur]); // eslint-disable-line react-hooks/exhaustive-deps
+ const closeDropdown = useCallback(
+ (onAfterClose?: () => void) => {
+ setDropdownVisible(false, onAfterClose);
+ onDropdownWillShow?.(false);
+ setSearchTxt('');
+ onBlur?.();
+ },
+ [onDropdownWillShow, setSearchTxt, onBlur],
+ ); // eslint-disable-line react-hooks/exhaustive-deps
/**
* Handle selecting an item.
@@ -396,22 +408,21 @@ export const Dropdown = typedForwardRef((props: DropdownProps, ref?: Ref<
// Extract props form the inner component to pass to touchable
const { style = Styles.dropdownButton, ...dropdownProps } = innerDropdownComponents.props ?? {};
- // Main touchable button
- const dropdownButton = (
-
- );
-
if (!spatialNavigatorExist)
return (
- {dropdownButton}
+
{dropdownWindow}
);
@@ -419,7 +430,16 @@ export const Dropdown = typedForwardRef((props: DropdownProps, ref?: Ref<
return (
- {() => dropdownButton}
+ {() => (
+
+ )}
{dropdownWindow}