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}