dirk-cfx-react 1.0.40 → 1.0.41

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/misc.ts","../../src/hooks/useNuiEvent.ts","../../src/providers/DirkProvider.tsx","../../src/hooks/useTornEdges.tsx"],"names":["jsx"],"mappings":";;;;;;;;;;;;;;;;;AAGO,IAAM,OAAO,MAAM;AAAC,CAAA;;;ACmBpB,IAAM,WAAA,GAAc,CACzB,MAAA,EACA,OAAA,KACG;AACH,EAAA,MAAM,YAAA,GAAyD,OAAO,IAAI,CAAA;AAG1E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,OAAA,GAAU,OAAA;AAAA,EACzB,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAA2C;AAChE,MAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAa,IAAA,KAAS,KAAA,CAAM,IAAA;AAE5C,MAAA,IAAI,aAAa,OAAA,EAAS;AACxB,QAAA,IAAI,gBAAgB,MAAA,EAAQ;AAC1B,UAAA,YAAA,CAAa,QAAS,IAAI,CAAA;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAEhD,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,aAAa,CAAA;AAAA,EAClE,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AACb;AChCA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,GAAA,EAAK,GAAG,CAAA;AAOlB,IAAM,WAAA,GAAc,MAAA,CAMxB,CAAC,GAAA,MAAS;AAAA,EACX,IAAA,EAAM,OAAA;AAAA,EACN,YAAA,EAAc,MAAA;AAAA,EACd,YAAA,EAAc,CAAA;AAAA,EACd,WAAA,EAAa,mCAAA;AAAA,EACb,aAAa;AACf,CAAA,CAAE,CAAA;ACjCK,SAAS,YAAA,GAAe;AAC7B,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,CAAC,KAAA,KAAU,MAAM,IAAI,CAAA;AAC9C,EAAA,OAAO,IAAA,KAAS,SAAS,mBAAA,GAAsB,EAAA;AACjD;AAGO,SAAS,iBAAA,GAAoB;AAClC,EAAA,uBACEA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,OAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,aAAA,EAAe,MAAA,EAAO;AAAA,MAC1E,aAAA,EAAY,MAAA;AAAA,MAEZ,QAAA,kBAAAA,GAAAA,CAAC,MAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,YAAO,EAAA,EAAG,kBAAA,EAAmB,CAAA,EAAE,MAAA,EAAO,CAAA,EAAE,MAAA,EAAO,KAAA,EAAM,MAAA,EAAO,QAAO,MAAA,EAElE,QAAA,EAAA;AAAA,wBAAAA,GAAAA;AAAA,UAAC,cAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,cAAA;AAAA,YACL,aAAA,EAAc,aAAA;AAAA,YACd,UAAA,EAAW,GAAA;AAAA,YACX,IAAA,EAAK,GAAA;AAAA,YACL,MAAA,EAAO;AAAA;AAAA,SACT;AAAA,wBAEAA,GAAAA;AAAA,UAAC,cAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,cAAA;AAAA,YACL,aAAA,EAAc,WAAA;AAAA,YACd,UAAA,EAAW,GAAA;AAAA,YACX,IAAA,EAAK,GAAA;AAAA,YACL,MAAA,EAAO;AAAA;AAAA,SACT;AAAA,wBAGAA,GAAAA,CAAC,SAAA,EAAA,EAAQ,EAAA,EAAG,QAAA,EAAS,KAAI,QAAA,EAAS,IAAA,EAAK,UAAA,EAAW,MAAA,EAAO,eAAA,EAAgB,CAAA;AAAA,wBAGzEA,GAAAA;AAAA,UAAC,mBAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAG,eAAA;AAAA,YACH,GAAA,EAAI,eAAA;AAAA,YACJ,KAAA,EAAM,IAAA;AAAA,YACN,gBAAA,EAAiB,GAAA;AAAA,YACjB,gBAAA,EAAiB,GAAA;AAAA,YACjB,MAAA,EAAO;AAAA;AAAA,SACT;AAAA,wBAGAA,IAAC,gBAAA,EAAA,EAAe,YAAA,EAAa,OAAM,EAAA,EAAG,WAAA,EAAY,QAAO,SAAA,EAAU,CAAA;AAAA,wBACnEA,GAAAA,CAAC,qBAAA,EAAA,EAAoB,IAAG,SAAA,EAAU,MAAA,EAAO,aACvC,QAAA,kBAAAA,GAAAA,CAAC,SAAA,EAAA,EAAQ,IAAA,EAAK,SAAQ,SAAA,EAAU,GAAA,EAAI,UAAS,KAAA,EAAM,MAAA,EAAO,SAAQ,CAAA,EACpE,CAAA;AAAA,wBAGAA,GAAAA,CAAC,cAAA,EAAA,EAAa,QAAA,EAAS,OAAA,EAAQ,QAAO,KAAA,EAAM,EAAA,EAAG,WAAA,EAAY,MAAA,EAAO,QAAA,EAAS,CAAA;AAAA,wBAG3EA,IAAC,SAAA,EAAA,EACC,QAAA,kBAAAA,IAAC,aAAA,EAAA,EAAY,EAAA,EAAG,UAAS,CAAA,EAC3B;AAAA,OAAA,EACF,CAAA,EACF;AAAA;AAAA,GACF;AAEJ","file":"index.js","sourcesContent":["export const isEnvBrowser = (): boolean => !(window as any).invokeNative;\r\n\r\n// Basic no operation function\r\nexport const noop = () => {};\r\n\r\nexport const splitFAString = (faString:string) => {\r\n const [prefix, newIcon] = faString.split('-');\r\n if (!prefix || !newIcon) return {prefix: 'fas', newIcon: 'question'};\r\n return {prefix, newIcon};\r\n}\r\n\r\nexport const numberToRoman = (num:number) => {\r\n const romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX'] \r\n return romanNumerals[num]\r\n}\r\n\r\nexport const copyToClipboard = (text:string) => {\r\n const el = document.createElement('textarea');\r\n el.value = text;\r\n document.body.appendChild(el);\r\n el.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(el);\r\n}\r\n\r\nexport const openLink = (url:string) => {\r\n if (isEnvBrowser()) {\r\n window.open(url, '_blank');\r\n } else {\r\n // @ts-expect-error -- invokeNative exists in NUI\r\n window.invokeNative('openLink', url);\r\n } \r\n}","import { MutableRefObject, useEffect, useRef } from \"react\";\r\nimport { noop } from \"../utils/misc\";\r\n\r\ninterface NuiMessageData<T = unknown> {\r\n action: string;\r\n data: T;\r\n}\r\n\r\ntype NuiHandlerSignature<T> = ( data: T) => void;\r\n\r\n/**\r\n * A hook that manage events listeners for receiving data from the client scripts\r\n * @param action The specific `action` that should be listened for.\r\n * @param handler The callback function that will handle data relayed by this hook\r\n *\r\n * @example\r\n * useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => {\r\n * // whatever logic you want\r\n * })\r\n *\r\n **/\r\n\r\nexport const useNuiEvent = <T = unknown>(\r\n action: string,\r\n handler: ( data: T) => void,\r\n) => {\r\n const savedHandler: MutableRefObject<NuiHandlerSignature<T>> = useRef(noop);\r\n\r\n // Make sure we handle for a reactive handler\r\n useEffect(() => {\r\n savedHandler.current = handler;\r\n }, [handler]);\r\n\r\n useEffect(() => {\r\n const eventListener = (event: MessageEvent<NuiMessageData<T>>) => {\r\n const { action: eventAction, data } = event.data;\r\n\r\n if (savedHandler.current) {\r\n if (eventAction === action) {\r\n savedHandler.current( data);\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener(\"message\", eventListener);\r\n // Remove Event Listener on component dirkup\r\n return () => window.removeEventListener(\"message\", eventListener);\r\n }, [action]);\r\n};\r\n","import '@mantine/core/styles.css';\r\nimport '@mantine/notifications/styles.css';\r\nimport './styles/fonts.css';\r\nimport './styles/scrollBar.css';\r\nimport './styles/tornEdge.css';\r\n\r\n\r\nimport { library } from \"@fortawesome/fontawesome-svg-core\";\r\nimport { fab } from \"@fortawesome/free-brands-svg-icons\";\r\nimport { far } from \"@fortawesome/free-regular-svg-icons\";\r\nimport { fas } from \"@fortawesome/free-solid-svg-icons\";\r\nimport { BackgroundImage, MantineColorShade, MantineProvider, MantineThemeOverride } from '@mantine/core';\r\nimport { isEnvBrowser, useAutoFetcher } from '@/utils';\r\nimport { create } from 'zustand';\r\nimport theme from '@/theme';\r\nimport { useEffect, useMemo } from 'react';\r\nlibrary.add(fas, far, fab);\r\n\r\nexport type DirkProviderProps = {\r\n fakeBackground?: boolean;\r\n children: React.ReactNode;\r\n}\r\n\r\nexport const useSettings = create<{\r\n game: 'rdr3' | 'fivem';\r\n primaryColor: string;\r\n primaryShade: number;\r\n itemImgPath: string;\r\n customTheme: Record<string, string[]>;\r\n}>((set) => ({\r\n game: 'fivem',\r\n primaryColor: 'dirk',\r\n primaryShade: 9,\r\n itemImgPath: 'https://assets.dirkcfx.com/items/',\r\n customTheme: {},\r\n}));\r\n\r\n\r\n\r\nexport function DirkProvider(props: DirkProviderProps) {\r\n const primaryColor = useSettings((data) => data.primaryColor);\r\n const primaryShade = useSettings((data) => data.primaryShade);\r\n const customTheme = useSettings((data) => data.customTheme);\r\n const game = useSettings((data) => data.game);\r\n // Memoize the merged theme to avoid unnecessary recalculations\r\n const mergedTheme = useMemo<MantineThemeOverride>(() => ({\r\n ...theme,\r\n primaryColor: primaryColor,\r\n primaryShade: primaryShade as MantineColorShade,\r\n colors: {\r\n ...theme.colors,\r\n ...customTheme, // Custom theme colors will override/extend base colors\r\n },\r\n }), [primaryColor, primaryShade, customTheme]);\r\n\r\n useEffect(() => {\r\n document.fonts.ready.then(() => {\r\n document.body.style.fontFamily = \r\n game === 'rdr3' ? '\"Red Dead\", sans-serif' :\r\n game === 'fivem' ? '\"Akrobat Regular\", sans-serif' :\r\n 'sans-serif';\r\n console.log(`Game set to ${game}, applied corresponding font family.: ${document.body.style.fontFamily}`);\r\n });\r\n }, [game]);\r\n\r\n\r\n useAutoFetcher();\r\n \r\n \r\n return (\r\n <MantineProvider theme={mergedTheme} defaultColorScheme='dark'>\r\n <Wrapper>\r\n {props.children}\r\n </Wrapper>\r\n </MantineProvider>\r\n );\r\n}\r\n\r\nfunction Wrapper({ children }: { children: React.ReactNode }) {\r\n const game = useSettings((data) => data.game);\r\n return isEnvBrowser() ? ( \r\n <BackgroundImage w='100vw' h='100vh' style={{overflow:'hidden'}}\r\n src={game === 'fivem' ?\r\n \"https://i.ytimg.com/vi/TOxuNbXrO28/maxresdefault.jpg\"\r\n : \"https://raw.githubusercontent.com/Jump-On-Studios/RedM-jo_libs/refs/heads/main/source-repositories/Menu/public/assets/images/background_dev.jpg\"}\r\n > \r\n {children}\r\n </BackgroundImage>\r\n ) : (\r\n <>{children}</>\r\n )\r\n}\r\n\r\n","import { useSettings } from \"@/providers\";\r\n\r\nexport function useTornEdges() {\r\n const game = useSettings((state) => state.game);\r\n return game === \"rdr3\" ? \"torn-edge-wrapper\" : \"\";\r\n}\r\n\r\n// Add this SVG to your layout/root component:\r\nexport function TornEdgeSVGFilter() {\r\n return (\r\n <svg\r\n style={{ position: \"absolute\", width: 0, height: 0, pointerEvents: \"none\" }}\r\n aria-hidden=\"true\"\r\n >\r\n <defs>\r\n <filter id=\"torn-edge-filter\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\r\n {/* Base fractal noise for tearing */}\r\n <feTurbulence\r\n type=\"fractalNoise\"\r\n baseFrequency=\"0.018 0.022\"\r\n numOctaves=\"5\"\r\n seed=\"9\"\r\n result=\"noise1\"\r\n />\r\n {/* Second noise layer for micro detail */}\r\n <feTurbulence\r\n type=\"fractalNoise\"\r\n baseFrequency=\"0.08 0.12\"\r\n numOctaves=\"2\"\r\n seed=\"3\"\r\n result=\"noise2\"\r\n />\r\n\r\n {/* Blend both noise layers */}\r\n <feBlend in=\"noise1\" in2=\"noise2\" mode=\"multiply\" result=\"combinedNoise\" />\r\n\r\n {/* Primary displacement */}\r\n <feDisplacementMap\r\n in=\"SourceGraphic\"\r\n in2=\"combinedNoise\"\r\n scale=\"52\"\r\n xChannelSelector=\"R\"\r\n yChannelSelector=\"G\"\r\n result=\"displaced\"\r\n />\r\n\r\n {/* Alpha fade toward edges */}\r\n <feGaussianBlur stdDeviation=\"0.8\" in=\"displaced\" result=\"blurred\" />\r\n <feComponentTransfer in=\"blurred\" result=\"alphaFade\">\r\n <feFuncA type=\"gamma\" amplitude=\"1\" exponent=\"1.3\" offset=\"-0.05\" />\r\n </feComponentTransfer>\r\n\r\n {/* Sharpen a bit after fade */}\r\n <feMorphology operator=\"erode\" radius=\"0.4\" in=\"alphaFade\" result=\"eroded\" />\r\n\r\n {/* Merge final */}\r\n <feMerge>\r\n <feMergeNode in=\"eroded\" />\r\n </feMerge>\r\n </filter>\r\n </defs>\r\n </svg>\r\n );\r\n}\r\n\r\n"]}
1
+ {"version":3,"sources":["../../src/utils/misc.ts","../../src/hooks/useNuiEvent.ts","../../src/hooks/useForm.tsx","../../src/providers/DirkProvider.tsx","../../src/hooks/useTornEdges.tsx"],"names":["state","useRef","jsx"],"mappings":"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","file":"index.js","sourcesContent":["export const isEnvBrowser = (): boolean => !(window as any).invokeNative;\r\n\r\n// Basic no operation function\r\nexport const noop = () => {};\r\n\r\nexport const splitFAString = (faString:string) => {\r\n const [prefix, newIcon] = faString.split('-');\r\n if (!prefix || !newIcon) return {prefix: 'fas', newIcon: 'question'};\r\n return {prefix, newIcon};\r\n}\r\n\r\nexport const numberToRoman = (num:number) => {\r\n const romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX'] \r\n return romanNumerals[num]\r\n}\r\n\r\nexport const copyToClipboard = (text:string) => {\r\n const el = document.createElement('textarea');\r\n el.value = text;\r\n document.body.appendChild(el);\r\n el.select();\r\n document.execCommand('copy');\r\n document.body.removeChild(el);\r\n}\r\n\r\nexport const openLink = (url:string) => {\r\n if (isEnvBrowser()) {\r\n window.open(url, '_blank');\r\n } else {\r\n // @ts-expect-error -- invokeNative exists in NUI\r\n window.invokeNative('openLink', url);\r\n } \r\n}","import { MutableRefObject, useEffect, useRef } from \"react\";\r\nimport { noop } from \"../utils/misc\";\r\n\r\ninterface NuiMessageData<T = unknown> {\r\n action: string;\r\n data: T;\r\n}\r\n\r\ntype NuiHandlerSignature<T> = ( data: T) => void;\r\n\r\n/**\r\n * A hook that manage events listeners for receiving data from the client scripts\r\n * @param action The specific `action` that should be listened for.\r\n * @param handler The callback function that will handle data relayed by this hook\r\n *\r\n * @example\r\n * useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => {\r\n * // whatever logic you want\r\n * })\r\n *\r\n **/\r\n\r\nexport const useNuiEvent = <T = unknown>(\r\n action: string,\r\n handler: ( data: T) => void,\r\n) => {\r\n const savedHandler: MutableRefObject<NuiHandlerSignature<T>> = useRef(noop);\r\n\r\n // Make sure we handle for a reactive handler\r\n useEffect(() => {\r\n savedHandler.current = handler;\r\n }, [handler]);\r\n\r\n useEffect(() => {\r\n const eventListener = (event: MessageEvent<NuiMessageData<T>>) => {\r\n const { action: eventAction, data } = event.data;\r\n\r\n if (savedHandler.current) {\r\n if (eventAction === action) {\r\n savedHandler.current( data);\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener(\"message\", eventListener);\r\n // Remove Event Listener on component dirkup\r\n return () => window.removeEventListener(\"message\", eventListener);\r\n }, [action]);\r\n};\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\n\r\nimport React, { createContext, useContext, useRef } from \"react\";\r\nimport { createStore, StoreApi, useStore } from \"zustand\";\r\n\r\n// ---- Utility functions ----\r\nfunction getNested(obj: any, path: string): any {\r\n return path.split(\".\").reduce((acc, key) => (acc ? acc[key] : undefined), obj);\r\n}\r\n\r\nfunction setNested(obj: any, path: string, value: any): any {\r\n const keys = path.split(\".\");\r\n const newObj = { ...obj };\r\n let current = newObj;\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i];\r\n current[key] = { ...(current[key] || {}) };\r\n current = current[key];\r\n }\r\n current[keys[keys.length - 1]] = value;\r\n return newObj;\r\n}\r\n\r\nfunction deleteNested(obj: any, path: string): any {\r\n const keys = path.split(\".\");\r\n const newObj = { ...obj };\r\n let current = newObj;\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n const key = keys[i];\r\n if (!current[key]) return obj;\r\n current[key] = { ...current[key] };\r\n current = current[key];\r\n }\r\n delete current[keys[keys.length - 1]];\r\n return newObj;\r\n}\r\n\r\nfunction flattenRules(\r\n rules: any,\r\n prefix = \"\"\r\n): Record<string, (value: any, values: any) => string | null> {\r\n const result: Record<string, (value: any, values: any) => string | null> = {};\r\n for (const key in rules) {\r\n const fullPath = prefix ? `${prefix}.${key}` : key;\r\n const val = rules[key];\r\n if (typeof val === \"function\") result[fullPath] = val;\r\n else if (typeof val === \"object\")\r\n Object.assign(result, flattenRules(val, fullPath));\r\n }\r\n return result;\r\n}\r\n\r\n// ---- Types ----\r\nexport type ValidationRules<T> = {\r\n [K in keyof T]?: T[K] extends object\r\n ? ValidationRules<T[K]>\r\n : (value: T[K], values: Partial<T>) => string | null;\r\n};\r\n\r\nexport type FormState<T> = {\r\n values: Partial<T>;\r\n initialValues: Partial<T>;\r\n errors: Record<string, string>;\r\n\r\n setValue: (path: string, value: any) => void;\r\n setInitialValues: (newInitialValues: Partial<T>) => void;\r\n setError: (path: string, message: string) => void;\r\n clearError: (path: string) => void;\r\n validate: () => boolean;\r\n validateField: (path: string) => boolean;\r\n reset: () => void;\r\n\r\n back: () => void;\r\n forward: () => void;\r\n canBack: boolean;\r\n canForward: boolean;\r\n\r\n changedFields: string[];\r\n changedCount: number;\r\n resetChangeCount: () => void;\r\n\r\n /** New additions **/\r\n onSubmit?: (form: FormState<T>) => void;\r\n submit: () => void;\r\n};\r\n\r\n// ---- Core Store ----\r\nexport function createFormStore<T>(\r\n initialValues: Partial<T>,\r\n validationRules?: ValidationRules<T>,\r\n onSubmit?: (form: FormState<T>) => void\r\n) {\r\n const flatRules = validationRules ? flattenRules(validationRules) : {};\r\n const history: Partial<T>[] = [];\r\n const future: Partial<T>[] = [];\r\n const changed = new Set<string>();\r\n\r\n return createStore<FormState<T>>((set, get) => ({\r\n initialValues,\r\n values: initialValues,\r\n errors: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n onSubmit,\r\n submit: () => {\r\n const state = get();\r\n const isValid = state.validate();\r\n if (isValid && state.onSubmit) state.onSubmit(get());\r\n },\r\n\r\n resetChangeCount: () => {\r\n changed.clear();\r\n set(() => ({\r\n changedFields: [],\r\n changedCount: 0,\r\n }));\r\n },\r\n\r\n setInitialValues: (newInitialValues) =>\r\n set({ initialValues: newInitialValues }),\r\n\r\n setValue: (path, value) => {\r\n const currentValues = get().values;\r\n const newValues = setNested(currentValues, path, value);\r\n const oldValue = getNested(get().initialValues, path);\r\n\r\n history.push(currentValues);\r\n future.length = 0;\r\n\r\n if (value !== oldValue) changed.add(path);\r\n else changed.delete(path);\r\n\r\n set({\r\n values: newValues,\r\n canBack: history.length > 0,\r\n canForward: false,\r\n changedFields: Array.from(changed),\r\n changedCount: changed.size,\r\n });\r\n\r\n const rule = flatRules[path];\r\n if (rule) {\r\n const error = rule(value, newValues);\r\n if (error)\r\n set((state) => ({ errors: setNested(state.errors, path, error) }));\r\n else set((state) => ({ errors: deleteNested(state.errors, path) }));\r\n }\r\n },\r\n\r\n setError: (path, message) =>\r\n set((state) => ({ errors: setNested(state.errors, path, message) })),\r\n clearError: (path) =>\r\n set((state) => ({ errors: deleteNested(state.errors, path) })),\r\n\r\n validateField: (path) => {\r\n const state = get();\r\n const rule = flatRules[path];\r\n if (!rule) return true;\r\n\r\n const value = getNested(state.values, path);\r\n const error = rule(value, state.values);\r\n if (error) {\r\n set((state) => ({ errors: setNested(state.errors, path, error) }));\r\n return false;\r\n } else {\r\n set((state) => ({ errors: deleteNested(state.errors, path) }));\r\n return true;\r\n }\r\n },\r\n\r\n validate: () => {\r\n const state = get();\r\n let isValid = true;\r\n let newErrors: Record<string, string> = {};\r\n\r\n for (const path in flatRules) {\r\n const rule = flatRules[path];\r\n const value = getNested(state.values, path);\r\n const error = rule(value, state.values);\r\n if (error) {\r\n isValid = false;\r\n newErrors = setNested(newErrors, path, error);\r\n }\r\n }\r\n\r\n set({ errors: newErrors });\r\n return isValid;\r\n },\r\n\r\n reset: () => {\r\n history.length = 0;\r\n future.length = 0;\r\n changed.clear();\r\n set({\r\n values: initialValues,\r\n errors: {},\r\n canBack: false,\r\n canForward: false,\r\n changedFields: [],\r\n changedCount: 0,\r\n });\r\n },\r\n\r\n back: () => {\r\n const state = get();\r\n if (history.length === 0) return;\r\n const prev = history.pop()!;\r\n future.push(state.values);\r\n\r\n changed.clear();\r\n const current = prev;\r\n const initial = get().initialValues;\r\n for (const key in current) {\r\n if (JSON.stringify(current[key]) !== JSON.stringify(initial[key]))\r\n changed.add(key);\r\n }\r\n\r\n set({\r\n values: prev,\r\n canBack: history.length > 0,\r\n canForward: true,\r\n changedFields: Array.from(changed),\r\n changedCount: changed.size,\r\n });\r\n },\r\n\r\n forward: () => {\r\n const state = get();\r\n if (future.length === 0) return;\r\n const next = future.pop()!;\r\n history.push(state.values);\r\n\r\n changed.clear();\r\n const current = next;\r\n const initial = get().initialValues;\r\n for (const key in current) {\r\n if (JSON.stringify(current[key]) !== JSON.stringify(initial[key]))\r\n changed.add(key);\r\n }\r\n\r\n set({\r\n values: next,\r\n canBack: true,\r\n canForward: future.length > 0,\r\n changedFields: Array.from(changed),\r\n changedCount: changed.size,\r\n });\r\n },\r\n }));\r\n}\r\n\r\n// ---- Context ----\r\nconst FormContext = createContext<StoreApi<FormState<any>> | null>(null);\r\n\r\nexport function FormProvider<T>({\r\n initialValues,\r\n validate,\r\n onSubmit,\r\n children,\r\n}: {\r\n initialValues: Partial<T>;\r\n validate?: ValidationRules<T>;\r\n onSubmit?: (form: FormState<T>) => void;\r\n children: React.ReactNode;\r\n}) {\r\n const storeRef = useRef(createFormStore<T>(initialValues, validate, onSubmit));\r\n return (\r\n <FormContext.Provider value={storeRef.current}>\r\n {children}\r\n </FormContext.Provider>\r\n );\r\n}\r\n\r\n// ---- Hook ----\r\nexport function useForm<T>() {\r\n const store = useContext(FormContext);\r\n if (!store) throw new Error(\"useForm must be used inside a <FormProvider>\");\r\n return useStore(store) as FormState<T>;\r\n}\r\n","import '@mantine/core/styles.css';\r\nimport '@mantine/notifications/styles.css';\r\nimport './styles/fonts.css';\r\nimport './styles/scrollBar.css';\r\nimport './styles/tornEdge.css';\r\n\r\n\r\nimport { library } from \"@fortawesome/fontawesome-svg-core\";\r\nimport { fab } from \"@fortawesome/free-brands-svg-icons\";\r\nimport { far } from \"@fortawesome/free-regular-svg-icons\";\r\nimport { fas } from \"@fortawesome/free-solid-svg-icons\";\r\nimport { BackgroundImage, MantineColorShade, MantineProvider, MantineThemeOverride } from '@mantine/core';\r\nimport { isEnvBrowser, useAutoFetcher } from '@/utils';\r\nimport { create } from 'zustand';\r\nimport theme from '@/theme';\r\nimport { useEffect, useMemo } from 'react';\r\nlibrary.add(fas, far, fab);\r\n\r\nexport type DirkProviderProps = {\r\n fakeBackground?: boolean;\r\n children: React.ReactNode;\r\n}\r\n\r\nexport const useSettings = create<{\r\n game: 'rdr3' | 'fivem';\r\n primaryColor: string;\r\n primaryShade: number;\r\n itemImgPath: string;\r\n customTheme: Record<string, string[]>;\r\n}>((set) => ({\r\n game: 'fivem',\r\n primaryColor: 'dirk',\r\n primaryShade: 9,\r\n itemImgPath: 'https://assets.dirkcfx.com/items/',\r\n customTheme: {},\r\n}));\r\n\r\n\r\n\r\nexport function DirkProvider(props: DirkProviderProps) {\r\n const primaryColor = useSettings((data) => data.primaryColor);\r\n const primaryShade = useSettings((data) => data.primaryShade);\r\n const customTheme = useSettings((data) => data.customTheme);\r\n const game = useSettings((data) => data.game);\r\n // Memoize the merged theme to avoid unnecessary recalculations\r\n const mergedTheme = useMemo<MantineThemeOverride>(() => ({\r\n ...theme,\r\n primaryColor: primaryColor,\r\n primaryShade: primaryShade as MantineColorShade,\r\n colors: {\r\n ...theme.colors,\r\n ...customTheme, // Custom theme colors will override/extend base colors\r\n },\r\n }), [primaryColor, primaryShade, customTheme]);\r\n\r\n useEffect(() => {\r\n document.fonts.ready.then(() => {\r\n document.body.style.fontFamily = \r\n game === 'rdr3' ? '\"Red Dead\", sans-serif' :\r\n game === 'fivem' ? '\"Akrobat Regular\", sans-serif' :\r\n 'sans-serif';\r\n console.log(`Game set to ${game}, applied corresponding font family.: ${document.body.style.fontFamily}`);\r\n });\r\n }, [game]);\r\n\r\n\r\n useAutoFetcher();\r\n \r\n \r\n return (\r\n <MantineProvider theme={mergedTheme} defaultColorScheme='dark'>\r\n <Wrapper>\r\n {props.children}\r\n </Wrapper>\r\n </MantineProvider>\r\n );\r\n}\r\n\r\nfunction Wrapper({ children }: { children: React.ReactNode }) {\r\n const game = useSettings((data) => data.game);\r\n return isEnvBrowser() ? ( \r\n <BackgroundImage w='100vw' h='100vh' style={{overflow:'hidden'}}\r\n src={game === 'fivem' ?\r\n \"https://i.ytimg.com/vi/TOxuNbXrO28/maxresdefault.jpg\"\r\n : \"https://raw.githubusercontent.com/Jump-On-Studios/RedM-jo_libs/refs/heads/main/source-repositories/Menu/public/assets/images/background_dev.jpg\"}\r\n > \r\n {children}\r\n </BackgroundImage>\r\n ) : (\r\n <>{children}</>\r\n )\r\n}\r\n\r\n","import { useSettings } from \"@/providers\";\r\n\r\nexport function useTornEdges() {\r\n const game = useSettings((state) => state.game);\r\n return game === \"rdr3\" ? \"torn-edge-wrapper\" : \"\";\r\n}\r\n\r\n// Add this SVG to your layout/root component:\r\nexport function TornEdgeSVGFilter() {\r\n return (\r\n <svg\r\n style={{ position: \"absolute\", width: 0, height: 0, pointerEvents: \"none\" }}\r\n aria-hidden=\"true\"\r\n >\r\n <defs>\r\n <filter id=\"torn-edge-filter\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\r\n {/* Base fractal noise for tearing */}\r\n <feTurbulence\r\n type=\"fractalNoise\"\r\n baseFrequency=\"0.018 0.022\"\r\n numOctaves=\"5\"\r\n seed=\"9\"\r\n result=\"noise1\"\r\n />\r\n {/* Second noise layer for micro detail */}\r\n <feTurbulence\r\n type=\"fractalNoise\"\r\n baseFrequency=\"0.08 0.12\"\r\n numOctaves=\"2\"\r\n seed=\"3\"\r\n result=\"noise2\"\r\n />\r\n\r\n {/* Blend both noise layers */}\r\n <feBlend in=\"noise1\" in2=\"noise2\" mode=\"multiply\" result=\"combinedNoise\" />\r\n\r\n {/* Primary displacement */}\r\n <feDisplacementMap\r\n in=\"SourceGraphic\"\r\n in2=\"combinedNoise\"\r\n scale=\"52\"\r\n xChannelSelector=\"R\"\r\n yChannelSelector=\"G\"\r\n result=\"displaced\"\r\n />\r\n\r\n {/* Alpha fade toward edges */}\r\n <feGaussianBlur stdDeviation=\"0.8\" in=\"displaced\" result=\"blurred\" />\r\n <feComponentTransfer in=\"blurred\" result=\"alphaFade\">\r\n <feFuncA type=\"gamma\" amplitude=\"1\" exponent=\"1.3\" offset=\"-0.05\" />\r\n </feComponentTransfer>\r\n\r\n {/* Sharpen a bit after fade */}\r\n <feMorphology operator=\"erode\" radius=\"0.4\" in=\"alphaFade\" result=\"eroded\" />\r\n\r\n {/* Merge final */}\r\n <feMerge>\r\n <feMergeNode in=\"eroded\" />\r\n </feMerge>\r\n </filter>\r\n </defs>\r\n </svg>\r\n );\r\n}\r\n\r\n"]}
package/dist/index.cjs CHANGED
@@ -3,6 +3,8 @@
3
3
  var reactFontawesome = require('@fortawesome/react-fontawesome');
4
4
  var core = require('@mantine/core');
5
5
  var react = require('react');
6
+ var zustand = require('zustand');
7
+ var jsxRuntime = require('react/jsx-runtime');
6
8
  require('@mantine/core/styles.css');
7
9
  require('@mantine/notifications/styles.css');
8
10
  require('./styles/fonts.css');
@@ -12,8 +14,6 @@ var fontawesomeSvgCore = require('@fortawesome/fontawesome-svg-core');
12
14
  var freeBrandsSvgIcons = require('@fortawesome/free-brands-svg-icons');
13
15
  var freeRegularSvgIcons = require('@fortawesome/free-regular-svg-icons');
14
16
  var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
15
- var zustand = require('zustand');
16
- var jsxRuntime = require('react/jsx-runtime');
17
17
  var framerMotion = require('framer-motion');
18
18
  var clickSoundUrl = require('./click_sound-PNCRRTM4.mp3');
19
19
  var hoverSoundUrl = require('./hover_sound-NBUA222C.mp3');
@@ -242,6 +242,197 @@ var useNuiEvent = (action, handler) => {
242
242
  return () => window.removeEventListener("message", eventListener);
243
243
  }, [action]);
244
244
  };
245
+ function getNested(obj, path) {
246
+ return path.split(".").reduce((acc, key) => acc ? acc[key] : void 0, obj);
247
+ }
248
+ function setNested(obj, path, value) {
249
+ const keys = path.split(".");
250
+ const newObj = { ...obj };
251
+ let current = newObj;
252
+ for (let i = 0; i < keys.length - 1; i++) {
253
+ const key = keys[i];
254
+ current[key] = { ...current[key] || {} };
255
+ current = current[key];
256
+ }
257
+ current[keys[keys.length - 1]] = value;
258
+ return newObj;
259
+ }
260
+ function deleteNested(obj, path) {
261
+ const keys = path.split(".");
262
+ const newObj = { ...obj };
263
+ let current = newObj;
264
+ for (let i = 0; i < keys.length - 1; i++) {
265
+ const key = keys[i];
266
+ if (!current[key]) return obj;
267
+ current[key] = { ...current[key] };
268
+ current = current[key];
269
+ }
270
+ delete current[keys[keys.length - 1]];
271
+ return newObj;
272
+ }
273
+ function flattenRules(rules, prefix = "") {
274
+ const result = {};
275
+ for (const key in rules) {
276
+ const fullPath = prefix ? `${prefix}.${key}` : key;
277
+ const val = rules[key];
278
+ if (typeof val === "function") result[fullPath] = val;
279
+ else if (typeof val === "object")
280
+ Object.assign(result, flattenRules(val, fullPath));
281
+ }
282
+ return result;
283
+ }
284
+ function createFormStore(initialValues, validationRules, onSubmit) {
285
+ const flatRules = validationRules ? flattenRules(validationRules) : {};
286
+ const history = [];
287
+ const future = [];
288
+ const changed = /* @__PURE__ */ new Set();
289
+ return zustand.createStore((set, get) => ({
290
+ initialValues,
291
+ values: initialValues,
292
+ errors: {},
293
+ canBack: false,
294
+ canForward: false,
295
+ changedFields: [],
296
+ changedCount: 0,
297
+ onSubmit,
298
+ submit: () => {
299
+ const state = get();
300
+ const isValid = state.validate();
301
+ if (isValid && state.onSubmit) state.onSubmit(get());
302
+ },
303
+ resetChangeCount: () => {
304
+ changed.clear();
305
+ set(() => ({
306
+ changedFields: [],
307
+ changedCount: 0
308
+ }));
309
+ },
310
+ setInitialValues: (newInitialValues) => set({ initialValues: newInitialValues }),
311
+ setValue: (path, value) => {
312
+ const currentValues = get().values;
313
+ const newValues = setNested(currentValues, path, value);
314
+ const oldValue = getNested(get().initialValues, path);
315
+ history.push(currentValues);
316
+ future.length = 0;
317
+ if (value !== oldValue) changed.add(path);
318
+ else changed.delete(path);
319
+ set({
320
+ values: newValues,
321
+ canBack: history.length > 0,
322
+ canForward: false,
323
+ changedFields: Array.from(changed),
324
+ changedCount: changed.size
325
+ });
326
+ const rule = flatRules[path];
327
+ if (rule) {
328
+ const error = rule(value, newValues);
329
+ if (error)
330
+ set((state) => ({ errors: setNested(state.errors, path, error) }));
331
+ else set((state) => ({ errors: deleteNested(state.errors, path) }));
332
+ }
333
+ },
334
+ setError: (path, message) => set((state) => ({ errors: setNested(state.errors, path, message) })),
335
+ clearError: (path) => set((state) => ({ errors: deleteNested(state.errors, path) })),
336
+ validateField: (path) => {
337
+ const state = get();
338
+ const rule = flatRules[path];
339
+ if (!rule) return true;
340
+ const value = getNested(state.values, path);
341
+ const error = rule(value, state.values);
342
+ if (error) {
343
+ set((state2) => ({ errors: setNested(state2.errors, path, error) }));
344
+ return false;
345
+ } else {
346
+ set((state2) => ({ errors: deleteNested(state2.errors, path) }));
347
+ return true;
348
+ }
349
+ },
350
+ validate: () => {
351
+ const state = get();
352
+ let isValid = true;
353
+ let newErrors = {};
354
+ for (const path in flatRules) {
355
+ const rule = flatRules[path];
356
+ const value = getNested(state.values, path);
357
+ const error = rule(value, state.values);
358
+ if (error) {
359
+ isValid = false;
360
+ newErrors = setNested(newErrors, path, error);
361
+ }
362
+ }
363
+ set({ errors: newErrors });
364
+ return isValid;
365
+ },
366
+ reset: () => {
367
+ history.length = 0;
368
+ future.length = 0;
369
+ changed.clear();
370
+ set({
371
+ values: initialValues,
372
+ errors: {},
373
+ canBack: false,
374
+ canForward: false,
375
+ changedFields: [],
376
+ changedCount: 0
377
+ });
378
+ },
379
+ back: () => {
380
+ const state = get();
381
+ if (history.length === 0) return;
382
+ const prev = history.pop();
383
+ future.push(state.values);
384
+ changed.clear();
385
+ const current = prev;
386
+ const initial = get().initialValues;
387
+ for (const key in current) {
388
+ if (JSON.stringify(current[key]) !== JSON.stringify(initial[key]))
389
+ changed.add(key);
390
+ }
391
+ set({
392
+ values: prev,
393
+ canBack: history.length > 0,
394
+ canForward: true,
395
+ changedFields: Array.from(changed),
396
+ changedCount: changed.size
397
+ });
398
+ },
399
+ forward: () => {
400
+ const state = get();
401
+ if (future.length === 0) return;
402
+ const next = future.pop();
403
+ history.push(state.values);
404
+ changed.clear();
405
+ const current = next;
406
+ const initial = get().initialValues;
407
+ for (const key in current) {
408
+ if (JSON.stringify(current[key]) !== JSON.stringify(initial[key]))
409
+ changed.add(key);
410
+ }
411
+ set({
412
+ values: next,
413
+ canBack: true,
414
+ canForward: future.length > 0,
415
+ changedFields: Array.from(changed),
416
+ changedCount: changed.size
417
+ });
418
+ }
419
+ }));
420
+ }
421
+ var FormContext = react.createContext(null);
422
+ function FormProvider({
423
+ initialValues,
424
+ validate,
425
+ onSubmit,
426
+ children
427
+ }) {
428
+ const storeRef = react.useRef(createFormStore(initialValues, validate, onSubmit));
429
+ return /* @__PURE__ */ jsxRuntime.jsx(FormContext.Provider, { value: storeRef.current, children });
430
+ }
431
+ function useForm() {
432
+ const store = react.useContext(FormContext);
433
+ if (!store) throw new Error("useForm must be used inside a <FormProvider>");
434
+ return zustand.useStore(store);
435
+ }
245
436
  async function fetchNui(eventName, data, mockData) {
246
437
  const options = {
247
438
  method: "post",
@@ -722,7 +913,7 @@ function getLevelFromXP(xp, levelMap, settings) {
722
913
  return settings.maxLevel;
723
914
  }
724
915
  function createSkill(defaultSettings) {
725
- const useStore3 = zustand.create((set) => ({
916
+ const useStore4 = zustand.create((set) => ({
726
917
  settings: defaultSettings,
727
918
  levelMap: generateLevelMap(defaultSettings),
728
919
  setSettings: (updater) => set((state) => {
@@ -734,7 +925,7 @@ function createSkill(defaultSettings) {
734
925
  })
735
926
  }));
736
927
  const useSkill = (xp) => {
737
- const { settings, levelMap } = useStore3();
928
+ const { settings, levelMap } = useStore4();
738
929
  return react.useMemo(() => {
739
930
  const currentLevel = getLevelFromXP(xp, levelMap, settings);
740
931
  const nextLevel = Math.min(currentLevel + 1, settings.maxLevel);
@@ -756,12 +947,12 @@ function createSkill(defaultSettings) {
756
947
  };
757
948
  const skill = {
758
949
  get settings() {
759
- return useStore3.getState().settings;
950
+ return useStore4.getState().settings;
760
951
  },
761
952
  setSettings: (updater) => {
762
- useStore3.getState().setSettings(updater);
953
+ useStore4.getState().setSettings(updater);
763
954
  },
764
- useSettings: () => useStore3((state) => state.settings)
955
+ useSettings: () => useStore4((state) => state.settings)
765
956
  };
766
957
  return {
767
958
  skill,
@@ -1911,6 +2102,7 @@ exports.BorderedIcon = BorderedIcon;
1911
2102
  exports.Counter = Counter;
1912
2103
  exports.DirkProvider = DirkProvider;
1913
2104
  exports.FloatingParticles = FloatingParticles;
2105
+ exports.FormProvider = FormProvider;
1914
2106
  exports.InfoBox = InfoBox;
1915
2107
  exports.InputContainer = InputContainer;
1916
2108
  exports.ModalContext = ModalContext;
@@ -1928,6 +2120,7 @@ exports.Title = Title;
1928
2120
  exports.TornEdgeSVGFilter = TornEdgeSVGFilter;
1929
2121
  exports.colorWithAlpha = colorWithAlpha;
1930
2122
  exports.copyToClipboard = copyToClipboard;
2123
+ exports.createFormStore = createFormStore;
1931
2124
  exports.createSkill = createSkill;
1932
2125
  exports.fetchNui = fetchNui;
1933
2126
  exports.initialFetches = initialFetches;
@@ -1943,6 +2136,7 @@ exports.registerInitialFetch = registerInitialFetch;
1943
2136
  exports.runFetches = runFetches;
1944
2137
  exports.splitFAString = splitFAString;
1945
2138
  exports.useAutoFetcher = useAutoFetcher;
2139
+ exports.useForm = useForm;
1946
2140
  exports.useModal = useModal;
1947
2141
  exports.useModalActions = useModalActions;
1948
2142
  exports.useNavigation = useNavigation;