dirk-cfx-react 1.1.64 → 1.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var core = require('@mantine/core');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
- var React5 = require('react');
5
+ var React6 = require('react');
6
6
  var zustand = require('zustand');
7
7
  var axios = require('axios');
8
8
  var reactFontawesome = require('@fortawesome/react-fontawesome');
@@ -24,7 +24,7 @@ var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
24
24
 
25
25
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
26
26
 
27
- var React5__default = /*#__PURE__*/_interopDefault(React5);
27
+ var React6__default = /*#__PURE__*/_interopDefault(React6);
28
28
  var axios__default = /*#__PURE__*/_interopDefault(axios);
29
29
  var clickSoundUrl__default = /*#__PURE__*/_interopDefault(clickSoundUrl);
30
30
  var hoverSoundUrl__default = /*#__PURE__*/_interopDefault(hoverSoundUrl);
@@ -1079,6 +1079,31 @@ function BlipColorSelect({ value, onChange, label: label2 = "Blip Color", size =
1079
1079
  }
1080
1080
  );
1081
1081
  }
1082
+ var BLIP_DISPLAY_DATA = [
1083
+ { value: "2", label: "2 \u2014 Main map + minimap (selectable)" },
1084
+ { value: "3", label: "3 \u2014 Main map only (selectable)" },
1085
+ { value: "4", label: "4 \u2014 Main map only (selectable)" },
1086
+ { value: "5", label: "5 \u2014 Minimap only" },
1087
+ { value: "6", label: "6 \u2014 Main map + minimap (selectable)" },
1088
+ { value: "8", label: "8 \u2014 Main map + minimap (not selectable)" },
1089
+ { value: "9", label: "9 \u2014 Minimap only" },
1090
+ { value: "10", label: "10 \u2014 Main map + minimap (not selectable)" }
1091
+ ];
1092
+ function BlipDisplaySelect({ value, onChange, label: label2 = "Blip Display", size = "xs", ...rest }) {
1093
+ return /* @__PURE__ */ jsxRuntime.jsx(
1094
+ core.Select,
1095
+ {
1096
+ label: label2,
1097
+ size,
1098
+ ...rest,
1099
+ data: BLIP_DISPLAY_DATA,
1100
+ value: value != null ? String(value) : null,
1101
+ onChange: (val) => val != null && onChange(Number(val)),
1102
+ allowDeselect: false,
1103
+ maxDropdownHeight: 300
1104
+ }
1105
+ );
1106
+ }
1082
1107
 
1083
1108
  // src/utils/colorWithAlpha.ts
1084
1109
  var colorNames = {
@@ -1225,11 +1250,11 @@ var colorNames = {
1225
1250
  Yellow: { r: 255, g: 255, b: 0 },
1226
1251
  YellowGreen: { r: 154, g: 205, b: 50 }
1227
1252
  };
1228
- function colorWithAlpha(color, alpha8) {
1253
+ function colorWithAlpha(color, alpha9) {
1229
1254
  const lowerCasedColor = color.toLowerCase();
1230
1255
  if (colorNames[lowerCasedColor]) {
1231
1256
  const rgb = colorNames[lowerCasedColor];
1232
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha8})`;
1257
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha9})`;
1233
1258
  }
1234
1259
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1235
1260
  const hex = color.slice(1);
@@ -1237,12 +1262,12 @@ function colorWithAlpha(color, alpha8) {
1237
1262
  const r = bigint >> 16 & 255;
1238
1263
  const g = bigint >> 8 & 255;
1239
1264
  const b = bigint & 255;
1240
- return `rgba(${r}, ${g}, ${b}, ${alpha8})`;
1265
+ return `rgba(${r}, ${g}, ${b}, ${alpha9})`;
1241
1266
  }
1242
1267
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1243
1268
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1244
1269
  if (result) {
1245
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha8})`;
1270
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha9})`;
1246
1271
  }
1247
1272
  }
1248
1273
  return color;
@@ -1337,7 +1362,7 @@ async function runFetches() {
1337
1362
  );
1338
1363
  }
1339
1364
  var useAutoFetcher = () => {
1340
- React5.useEffect(() => {
1365
+ React6.useEffect(() => {
1341
1366
  if (isEnvBrowser()) return;
1342
1367
  runFetches().catch(() => {
1343
1368
  });
@@ -1833,7 +1858,7 @@ function createSkill(defaultSettings) {
1833
1858
  }));
1834
1859
  const useSkill = (xp) => {
1835
1860
  const { settings, levelMap } = useStore4();
1836
- return React5.useMemo(() => {
1861
+ return React6.useMemo(() => {
1837
1862
  const currentLevel = getLevelFromXP(xp, levelMap, settings);
1838
1863
  const nextLevel = Math.min(currentLevel + 1, settings.maxLevel);
1839
1864
  const currentLevelXP = levelMap[currentLevel.toString()] || 0;
@@ -1921,6 +1946,23 @@ registerInitialFetch("FETCH_ALL_ITEMS", null, {
1921
1946
  useItems.setState(fetchedItems);
1922
1947
  }).catch(() => {
1923
1948
  });
1949
+ var useFrameworkGroups = zustand.create(() => ({
1950
+ jobs: [],
1951
+ gangs: [],
1952
+ loaded: false
1953
+ }));
1954
+ registerInitialFetch("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
1955
+ useFrameworkGroups.setState({
1956
+ jobs: Array.isArray(data?.jobs) ? data.jobs : [],
1957
+ gangs: Array.isArray(data?.gangs) ? data.gangs : [],
1958
+ loaded: true
1959
+ });
1960
+ }).catch(() => {
1961
+ useFrameworkGroups.setState({ loaded: true });
1962
+ });
1963
+ function selectAllGroups(state) {
1964
+ return [...state.jobs, ...state.gangs];
1965
+ }
1924
1966
 
1925
1967
  // src/utils/inputMapper.ts
1926
1968
  var INPUT_MAPPER_PRIMARY_OPTIONS = [
@@ -2290,8 +2332,8 @@ var FloatingParticles = ({
2290
2332
  mouseRepelStrength = 50,
2291
2333
  backgroundColor = "transparent"
2292
2334
  }) => {
2293
- const containerRef = React5.useRef(null);
2294
- const [particles, setParticles] = React5.useState([]);
2335
+ const containerRef = React6.useRef(null);
2336
+ const [particles, setParticles] = React6.useState([]);
2295
2337
  const mouseX = framerMotion.useMotionValue(0);
2296
2338
  const mouseY = framerMotion.useMotionValue(0);
2297
2339
  const durationMap = {
@@ -2304,7 +2346,7 @@ var FloatingParticles = ({
2304
2346
  const x = Math.sin(seed) * 1e4;
2305
2347
  return x - Math.floor(x);
2306
2348
  };
2307
- React5.useEffect(() => {
2349
+ React6.useEffect(() => {
2308
2350
  if (!containerRef.current) return;
2309
2351
  const bounds = containerRef.current.getBoundingClientRect();
2310
2352
  const newParticles = [...Array(particleCount)].map((_, i) => {
@@ -2325,7 +2367,7 @@ var FloatingParticles = ({
2325
2367
  });
2326
2368
  setParticles(newParticles);
2327
2369
  }, [particleCount, icons.length, duration.base, duration.variance]);
2328
- React5.useEffect(() => {
2370
+ React6.useEffect(() => {
2329
2371
  if (!containerRef.current) return;
2330
2372
  const handleMouseMove = (e) => {
2331
2373
  const bounds = containerRef.current.getBoundingClientRect();
@@ -2374,7 +2416,7 @@ var FloatingParticles = ({
2374
2416
  container.removeEventListener("mouseleave", handleMouseLeave);
2375
2417
  };
2376
2418
  }, [mouseX, mouseY, mouseRepelDistance, mouseRepelStrength]);
2377
- React5.useEffect(() => {
2419
+ React6.useEffect(() => {
2378
2420
  const handleResize = () => {
2379
2421
  if (!containerRef.current) return;
2380
2422
  const bounds = containerRef.current.getBoundingClientRect();
@@ -2810,23 +2852,23 @@ function Segment(props) {
2810
2852
  }
2811
2853
  );
2812
2854
  }
2813
- var NavigationContext = React5.createContext(null);
2855
+ var NavigationContext = React6.createContext(null);
2814
2856
  function useNavigation(selector) {
2815
- const navigation = React5.useContext(NavigationContext);
2857
+ const navigation = React6.useContext(NavigationContext);
2816
2858
  if (!navigation) {
2817
2859
  throw new Error("useNavigation must be used within a NavigationProvider");
2818
2860
  }
2819
2861
  return zustand.useStore(navigation, selector);
2820
2862
  }
2821
2863
  function useNavigationStore() {
2822
- const navigation = React5.useContext(NavigationContext);
2864
+ const navigation = React6.useContext(NavigationContext);
2823
2865
  if (!navigation) {
2824
2866
  throw new Error("useNavigationStore must be used within a NavigationProvider");
2825
2867
  }
2826
2868
  return navigation;
2827
2869
  }
2828
2870
  function NavigationProvider({ children, defaultPage }) {
2829
- const storeRef = React5.useRef(
2871
+ const storeRef = React6.useRef(
2830
2872
  zustand.create(() => ({
2831
2873
  pageId: defaultPage || "home"
2832
2874
  }))
@@ -3183,7 +3225,7 @@ function Modal({
3183
3225
  children
3184
3226
  }) {
3185
3227
  const theme2 = core.useMantineTheme();
3186
- const pointerDownOnOverlay = React5.useRef(false);
3228
+ const pointerDownOnOverlay = React6.useRef(false);
3187
3229
  return /* @__PURE__ */ jsxRuntime.jsx(core.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3188
3230
  framerMotion.motion.div,
3189
3231
  {
@@ -3364,9 +3406,9 @@ function PromptModal(props) {
3364
3406
  }
3365
3407
  );
3366
3408
  }
3367
- var ModalContext = React5.createContext(null);
3409
+ var ModalContext = React6.createContext(null);
3368
3410
  function useModal(selector) {
3369
- const modal = React5.useContext(ModalContext);
3411
+ const modal = React6.useContext(ModalContext);
3370
3412
  if (!modal) {
3371
3413
  throw new Error("useModal must be used within a ModalProvider");
3372
3414
  }
@@ -3394,7 +3436,7 @@ function StoreModal() {
3394
3436
  ) });
3395
3437
  }
3396
3438
  function ModalProvider({ children }) {
3397
- const storeRef = React5.useRef(
3439
+ const storeRef = React6.useRef(
3398
3440
  zustand.create(() => ({
3399
3441
  active: null
3400
3442
  }))
@@ -3405,7 +3447,7 @@ function ModalProvider({ children }) {
3405
3447
  ] });
3406
3448
  }
3407
3449
  function useModalActions() {
3408
- const modal = React5.useContext(ModalContext);
3450
+ const modal = React6.useContext(ModalContext);
3409
3451
  if (!modal) throw new Error("useModalActions must be used within a ModalProvider");
3410
3452
  const showModal = (openModal) => {
3411
3453
  modal.setState({ active: openModal });
@@ -3425,7 +3467,7 @@ function ConfirmModal({
3425
3467
  zIndex = 200
3426
3468
  }) {
3427
3469
  const theme2 = core.useMantineTheme();
3428
- const [typed, setTyped] = React5.useState("");
3470
+ const [typed, setTyped] = React6.useState("");
3429
3471
  const canConfirm = !confirmText || typed === confirmText;
3430
3472
  return /* @__PURE__ */ jsxRuntime.jsx(core.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3431
3473
  framerMotion.motion.div,
@@ -3903,38 +3945,38 @@ function createFormStore(initialValues, validationRules, onSubmit) {
3903
3945
  }
3904
3946
  }));
3905
3947
  }
3906
- var FormContext = React5.createContext(null);
3948
+ var FormContext = React6.createContext(null);
3907
3949
  function FormProvider({
3908
3950
  initialValues,
3909
3951
  validate,
3910
3952
  onSubmit,
3911
3953
  children
3912
3954
  }) {
3913
- const storeRef = React5.useRef(
3955
+ const storeRef = React6.useRef(
3914
3956
  createFormStore(initialValues, validate, onSubmit)
3915
3957
  );
3916
3958
  return /* @__PURE__ */ jsxRuntime.jsx(FormContext.Provider, { value: storeRef.current, children });
3917
3959
  }
3918
3960
  function useForm() {
3919
- const store = React5.useContext(FormContext);
3961
+ const store = React6.useContext(FormContext);
3920
3962
  if (!store) {
3921
3963
  throw new Error("useForm must be used inside <FormProvider>");
3922
3964
  }
3923
3965
  const state = zustand.useStore(store);
3924
- const changedFields = React5.useMemo(() => {
3966
+ const changedFields = React6.useMemo(() => {
3925
3967
  return collectChangedPaths(state.values, state.initialValues);
3926
3968
  }, [state.values, state.initialValues]);
3927
3969
  return { ...state, changedFields, changedCount: changedFields.length };
3928
3970
  }
3929
3971
  function useFormField(path) {
3930
- const store = React5.useContext(FormContext);
3972
+ const store = React6.useContext(FormContext);
3931
3973
  if (!store) {
3932
3974
  throw new Error("useFormField must be used inside <FormProvider>");
3933
3975
  }
3934
3976
  return zustand.useStore(store, (s) => getNested(s.values, path));
3935
3977
  }
3936
3978
  function useFormFields(...paths) {
3937
- const store = React5.useContext(FormContext);
3979
+ const store = React6.useContext(FormContext);
3938
3980
  if (!store) {
3939
3981
  throw new Error("useFormFields must be used inside <FormProvider>");
3940
3982
  }
@@ -3947,14 +3989,14 @@ function useFormFields(...paths) {
3947
3989
  });
3948
3990
  }
3949
3991
  function useFormError(path) {
3950
- const store = React5.useContext(FormContext);
3992
+ const store = React6.useContext(FormContext);
3951
3993
  if (!store) {
3952
3994
  throw new Error("useFormError must be used inside <FormProvider>");
3953
3995
  }
3954
3996
  return zustand.useStore(store, (s) => s.errors[path]);
3955
3997
  }
3956
3998
  function useFormErrors(...paths) {
3957
- const store = React5.useContext(FormContext);
3999
+ const store = React6.useContext(FormContext);
3958
4000
  if (!store) {
3959
4001
  throw new Error("useFormErrors must be used inside <FormProvider>");
3960
4002
  }
@@ -3967,18 +4009,18 @@ function useFormErrors(...paths) {
3967
4009
  });
3968
4010
  }
3969
4011
  function useFormActions() {
3970
- const store = React5.useContext(FormContext);
4012
+ const store = React6.useContext(FormContext);
3971
4013
  if (!store) {
3972
4014
  throw new Error("useFormActions must be used inside <FormProvider>");
3973
4015
  }
3974
4016
  return store.getState();
3975
4017
  }
3976
4018
  var useNuiEvent = (action, handler) => {
3977
- const savedHandler = React5.useRef(noop);
3978
- React5.useEffect(() => {
4019
+ const savedHandler = React6.useRef(noop);
4020
+ React6.useEffect(() => {
3979
4021
  savedHandler.current = handler;
3980
4022
  }, [handler]);
3981
- React5.useEffect(() => {
4023
+ React6.useEffect(() => {
3982
4024
  const eventListener = (event) => {
3983
4025
  const { action: eventAction, data } = event.data;
3984
4026
  if (savedHandler.current) {
@@ -4111,8 +4153,8 @@ function ConfigJsonModal({
4111
4153
  const theme2 = core.useMantineTheme();
4112
4154
  const color = theme2.colors[theme2.primaryColor][5];
4113
4155
  const form = useForm();
4114
- const [json, setJson] = React5.useState(() => JSON.stringify(form.values, null, 2));
4115
- const [error2, setError] = React5.useState(null);
4156
+ const [json, setJson] = React6.useState(() => JSON.stringify(form.values, null, 2));
4157
+ const [error2, setError] = React6.useState(null);
4116
4158
  const handleSave = () => {
4117
4159
  try {
4118
4160
  const parsed = JSON.parse(json);
@@ -4289,14 +4331,14 @@ function ConfigHistoryModal({
4289
4331
  const { getHistory } = getScriptConfigInstance();
4290
4332
  const theme2 = core.useMantineTheme();
4291
4333
  const color = theme2.colors[theme2.primaryColor][5];
4292
- const [queryInput, setQueryInput] = React5.useState("");
4293
- const [pathInput, setPathInput] = React5.useState("");
4294
- const [adminInput, setAdminInput] = React5.useState("");
4295
- const [query, setQuery] = React5.useState("");
4296
- const [path, setPath] = React5.useState("");
4297
- const [admin, setAdmin] = React5.useState("");
4298
- const [expandedKey, setExpandedKey] = React5.useState(null);
4299
- const filters = React5.useMemo(() => ({ query, path, admin }), [query, path, admin]);
4334
+ const [queryInput, setQueryInput] = React6.useState("");
4335
+ const [pathInput, setPathInput] = React6.useState("");
4336
+ const [adminInput, setAdminInput] = React6.useState("");
4337
+ const [query, setQuery] = React6.useState("");
4338
+ const [path, setPath] = React6.useState("");
4339
+ const [admin, setAdmin] = React6.useState("");
4340
+ const [expandedKey, setExpandedKey] = React6.useState(null);
4341
+ const filters = React6.useMemo(() => ({ query, path, admin }), [query, path, admin]);
4300
4342
  const historyQuery = reactQuery.useInfiniteQuery({
4301
4343
  queryKey: ["scriptConfigHistory", filters],
4302
4344
  initialPageParam: 0,
@@ -4390,12 +4432,12 @@ function ConfigPanelInner({
4390
4432
  const theme2 = core.useMantineTheme();
4391
4433
  const color = theme2.colors[theme2.primaryColor][5];
4392
4434
  const version = useSettings((s) => s.resourceVersion);
4393
- const [activeTab, setActiveTab] = React5.useState(navItems[0]?.id ?? "");
4394
- const firstMountRef = React5.useRef(true);
4395
- const [jsonOpen, setJsonOpen] = React5.useState(false);
4396
- const [historyOpen, setHistoryOpen] = React5.useState(false);
4397
- const [resetOpen, setResetOpen] = React5.useState(false);
4398
- const [pendingAction, setPendingAction] = React5.useState(null);
4435
+ const [activeTab, setActiveTab] = React6.useState(navItems[0]?.id ?? "");
4436
+ const firstMountRef = React6.useRef(true);
4437
+ const [jsonOpen, setJsonOpen] = React6.useState(false);
4438
+ const [historyOpen, setHistoryOpen] = React6.useState(false);
4439
+ const [resetOpen, setResetOpen] = React6.useState(false);
4440
+ const [pendingAction, setPendingAction] = React6.useState(null);
4399
4441
  const changedCount = form.changedCount ?? 0;
4400
4442
  const isDirty = changedCount > 0;
4401
4443
  const goBack = () => fetchNui("CONFIG_PANEL_BACK");
@@ -4406,7 +4448,7 @@ function ConfigPanelInner({
4406
4448
  }
4407
4449
  goBack();
4408
4450
  };
4409
- React5.useEffect(() => {
4451
+ React6.useEffect(() => {
4410
4452
  function handleKeyDown(e) {
4411
4453
  if (e.key !== "Escape") return;
4412
4454
  if (isDirty) {
@@ -4629,7 +4671,7 @@ function cloneConfig(value) {
4629
4671
  function ServerOnlyFetcher() {
4630
4672
  const { fetchConfig } = getScriptConfigInstance();
4631
4673
  const { reinitialize } = useFormActions();
4632
- React5.useEffect(() => {
4674
+ React6.useEffect(() => {
4633
4675
  let cancelled = false;
4634
4676
  fetchConfig().then((full) => {
4635
4677
  if (!cancelled && full) reinitialize(full);
@@ -4645,7 +4687,7 @@ var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
4645
4687
  function ConfigPanel(props) {
4646
4688
  const { open, onClose = defaultOnClose } = props;
4647
4689
  const { store, updateConfig } = getScriptConfigInstance();
4648
- const [isSaving, setIsSaving] = React5.useState(false);
4690
+ const [isSaving, setIsSaving] = React6.useState(false);
4649
4691
  if (!open) return null;
4650
4692
  return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: configPanelQueryClient, children: /* @__PURE__ */ jsxRuntime.jsxs(
4651
4693
  FormProvider,
@@ -4684,9 +4726,9 @@ function ConfigPanel(props) {
4684
4726
  ) });
4685
4727
  }
4686
4728
  function LazyImage({ src, alt, style }) {
4687
- const [visible, setVisible] = React5.useState(false);
4688
- const ref = React5.useRef(null);
4689
- React5.useEffect(() => {
4729
+ const [visible, setVisible] = React6.useState(false);
4730
+ const ref = React6.useRef(null);
4731
+ React6.useEffect(() => {
4690
4732
  const observer = new IntersectionObserver(([entry]) => {
4691
4733
  if (entry.isIntersecting) {
4692
4734
  setVisible(true);
@@ -4700,7 +4742,7 @@ function LazyImage({ src, alt, style }) {
4700
4742
  }
4701
4743
  function SelectItem(props) {
4702
4744
  const invItems = useItems();
4703
- const formattedItems = React5.useMemo(() => {
4745
+ const formattedItems = React6.useMemo(() => {
4704
4746
  const seen = /* @__PURE__ */ new Set();
4705
4747
  return useItemsList(props.excludeItemNames ?? []).filter((item) => {
4706
4748
  if (seen.has(item.name)) return false;
@@ -4752,9 +4794,310 @@ function SelectItem(props) {
4752
4794
  }
4753
4795
  );
4754
4796
  }
4755
- var KeyBindContext = React5.createContext(null);
4797
+ var ZERO = { x: 0, y: 0, z: 0, w: 0 };
4798
+ function PositionPicker(props) {
4799
+ const {
4800
+ label: label2,
4801
+ value,
4802
+ onChange,
4803
+ relativeTo,
4804
+ fetchEvent = "GET_POSITION",
4805
+ previewEvent = "PREVIEW_POSITION",
4806
+ stopPreviewEvent = "STOP_PREVIEW_POSITION",
4807
+ description: description2,
4808
+ showHeading = true
4809
+ } = props;
4810
+ const theme2 = core.useMantineTheme();
4811
+ const color = theme2.colors[theme2.primaryColor][5];
4812
+ const [previewing, setPreviewing] = React6.useState(false);
4813
+ const previewingRef = React6.useRef(false);
4814
+ React6.useEffect(() => {
4815
+ return () => {
4816
+ if (previewingRef.current) {
4817
+ fetchNui(stopPreviewEvent, { relativeTo }).catch(() => {
4818
+ });
4819
+ }
4820
+ };
4821
+ }, [stopPreviewEvent, relativeTo]);
4822
+ const set = (key, val) => {
4823
+ const next = { ...value, [key]: val };
4824
+ onChange(next);
4825
+ if (previewingRef.current) {
4826
+ fetchNui(previewEvent, { value: next, relativeTo }).catch(() => {
4827
+ });
4828
+ }
4829
+ };
4830
+ const grab = async () => {
4831
+ try {
4832
+ const resp = await fetchNui(fetchEvent, { relativeTo }, value);
4833
+ if (resp && typeof resp === "object") {
4834
+ const next = {
4835
+ x: Number(resp.x ?? 0),
4836
+ y: Number(resp.y ?? 0),
4837
+ z: Number(resp.z ?? 0),
4838
+ w: Number(resp.w ?? 0)
4839
+ };
4840
+ onChange(next);
4841
+ if (previewingRef.current) {
4842
+ fetchNui(previewEvent, { value: next, relativeTo }).catch(() => {
4843
+ });
4844
+ }
4845
+ }
4846
+ } catch {
4847
+ }
4848
+ };
4849
+ const togglePreview = async () => {
4850
+ const nextState = !previewing;
4851
+ setPreviewing(nextState);
4852
+ previewingRef.current = nextState;
4853
+ if (nextState) {
4854
+ await fetchNui(previewEvent, { value, relativeTo }).catch(() => {
4855
+ });
4856
+ } else {
4857
+ await fetchNui(stopPreviewEvent, { relativeTo }).catch(() => {
4858
+ });
4859
+ }
4860
+ };
4861
+ const reset = () => {
4862
+ onChange({ ...ZERO });
4863
+ if (previewingRef.current) {
4864
+ fetchNui(previewEvent, { value: ZERO, relativeTo }).catch(() => {
4865
+ });
4866
+ }
4867
+ };
4868
+ const numberStyles = {
4869
+ input: { textAlign: "right", fontFamily: "monospace" }
4870
+ };
4871
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4872
+ core.Flex,
4873
+ {
4874
+ direction: "column",
4875
+ gap: "xxs",
4876
+ p: "xs",
4877
+ style: {
4878
+ background: core.alpha(theme2.colors.dark[5], 0.35),
4879
+ border: "0.1vh solid rgba(255,255,255,0.05)",
4880
+ borderRadius: theme2.radius.xs
4881
+ },
4882
+ children: [
4883
+ (label2 || description2) && /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { justify: "space-between", align: "center", gap: "xs", children: [
4884
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: 0, style: { minWidth: 0 }, children: [
4885
+ label2 && /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xxs", children: [
4886
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { size: "1.4vh", color: core.alpha(color, 0.7) }),
4887
+ /* @__PURE__ */ jsxRuntime.jsx(
4888
+ core.Text,
4889
+ {
4890
+ ff: "Akrobat Bold",
4891
+ size: "xxs",
4892
+ tt: "uppercase",
4893
+ lts: "0.05em",
4894
+ c: "rgba(255,255,255,0.75)",
4895
+ children: locale(label2)
4896
+ }
4897
+ ),
4898
+ relativeTo && /* @__PURE__ */ jsxRuntime.jsxs(core.Text, { ff: "Akrobat Bold", size: "xxs", c: core.alpha(color, 0.5), lts: "0.05em", children: [
4899
+ "\xB7 ",
4900
+ locale("RelativeTo"),
4901
+ " ",
4902
+ relativeTo
4903
+ ] })
4904
+ ] }),
4905
+ description2 && /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "dimmed", lh: 1.3, children: locale(description2) })
4906
+ ] }),
4907
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { gap: "xxs", style: { flexShrink: 0 }, children: [
4908
+ /* @__PURE__ */ jsxRuntime.jsx(PickerButton, { tooltip: locale("GrabMyPosition"), onClick: grab, color, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Crosshair, { size: "1.4vh", color }) }),
4909
+ /* @__PURE__ */ jsxRuntime.jsx(
4910
+ PickerButton,
4911
+ {
4912
+ tooltip: previewing ? locale("StopPreview") : locale("PreviewInWorld"),
4913
+ onClick: togglePreview,
4914
+ color,
4915
+ active: previewing,
4916
+ children: previewing ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { size: "1.4vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: "1.4vh", color: core.alpha(color, 0.7) })
4917
+ }
4918
+ ),
4919
+ /* @__PURE__ */ jsxRuntime.jsx(PickerButton, { tooltip: locale("Reset"), onClick: reset, color: "#ef4444", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { size: "1.4vh", color: "#ef4444" }) })
4920
+ ] })
4921
+ ] }),
4922
+ /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { gap: "xxs", children: [
4923
+ /* @__PURE__ */ jsxRuntime.jsx(
4924
+ core.NumberInput,
4925
+ {
4926
+ size: "xs",
4927
+ label: "X",
4928
+ value: value.x,
4929
+ onChange: (v) => set("x", Number(v)),
4930
+ decimalScale: 4,
4931
+ step: 0.1,
4932
+ style: { flex: 1 },
4933
+ styles: numberStyles
4934
+ }
4935
+ ),
4936
+ /* @__PURE__ */ jsxRuntime.jsx(
4937
+ core.NumberInput,
4938
+ {
4939
+ size: "xs",
4940
+ label: "Y",
4941
+ value: value.y,
4942
+ onChange: (v) => set("y", Number(v)),
4943
+ decimalScale: 4,
4944
+ step: 0.1,
4945
+ style: { flex: 1 },
4946
+ styles: numberStyles
4947
+ }
4948
+ ),
4949
+ /* @__PURE__ */ jsxRuntime.jsx(
4950
+ core.NumberInput,
4951
+ {
4952
+ size: "xs",
4953
+ label: "Z",
4954
+ value: value.z,
4955
+ onChange: (v) => set("z", Number(v)),
4956
+ decimalScale: 4,
4957
+ step: 0.1,
4958
+ style: { flex: 1 },
4959
+ styles: numberStyles
4960
+ }
4961
+ ),
4962
+ showHeading && /* @__PURE__ */ jsxRuntime.jsx(
4963
+ core.NumberInput,
4964
+ {
4965
+ size: "xs",
4966
+ label: "W",
4967
+ value: value.w,
4968
+ onChange: (v) => set("w", Number(v)),
4969
+ decimalScale: 2,
4970
+ step: 1,
4971
+ min: 0,
4972
+ max: 360,
4973
+ style: { flex: 1 },
4974
+ styles: numberStyles
4975
+ }
4976
+ )
4977
+ ] })
4978
+ ]
4979
+ }
4980
+ );
4981
+ }
4982
+ function PickerButton({
4983
+ children,
4984
+ onClick,
4985
+ tooltip,
4986
+ color,
4987
+ active
4988
+ }) {
4989
+ const theme2 = core.useMantineTheme();
4990
+ return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: tooltip, position: "top", withArrow: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4991
+ framerMotion.motion.button,
4992
+ {
4993
+ onClick,
4994
+ whileHover: { background: core.alpha(color, 0.18) },
4995
+ whileTap: { scale: 0.95 },
4996
+ style: {
4997
+ background: active ? core.alpha(color, 0.22) : core.alpha(color, 0.08),
4998
+ border: `0.1vh solid ${core.alpha(color, active ? 0.5 : 0.3)}`,
4999
+ borderRadius: theme2.radius.xs,
5000
+ padding: "0.4vh 0.6vh",
5001
+ cursor: "pointer",
5002
+ display: "flex",
5003
+ alignItems: "center",
5004
+ justifyContent: "center"
5005
+ },
5006
+ children
5007
+ }
5008
+ ) });
5009
+ }
5010
+ var GroupSelectContext = React6.createContext(null);
5011
+ function GroupSelect({
5012
+ value,
5013
+ onChange,
5014
+ type,
5015
+ children,
5016
+ style
5017
+ }) {
5018
+ return /* @__PURE__ */ jsxRuntime.jsx(GroupSelectContext.Provider, { value: { value, onChange, type }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.4vh", ...style }, children }) });
5019
+ }
5020
+ function filterByType(jobs, gangs, type) {
5021
+ if (type === "job") return jobs;
5022
+ if (type === "gang") return gangs;
5023
+ return [...jobs, ...gangs];
5024
+ }
5025
+ function GroupName(props) {
5026
+ const ctx = React6.useContext(GroupSelectContext);
5027
+ const jobs = useFrameworkGroups((s) => s.jobs);
5028
+ const gangs = useFrameworkGroups((s) => s.gangs);
5029
+ const inCompound = ctx !== null;
5030
+ const currentValue = inCompound ? ctx.value.name : props.value;
5031
+ const filterType = inCompound ? ctx.type : props.type;
5032
+ const list = filterByType(jobs, gangs, filterType);
5033
+ const data = filterType === void 0 ? [
5034
+ { group: locale("Jobs") || "Jobs", items: jobs.map((g) => ({ value: g.name, label: g.label })) },
5035
+ { group: locale("Gangs") || "Gangs", items: gangs.map((g) => ({ value: g.name, label: g.label })) }
5036
+ ] : list.map((g) => ({ value: g.name, label: g.label }));
5037
+ return /* @__PURE__ */ jsxRuntime.jsx(
5038
+ core.Select,
5039
+ {
5040
+ label: props.label,
5041
+ description: props.description,
5042
+ placeholder: props.placeholder ?? (locale("SelectGroup") || "Select\u2026"),
5043
+ size: props.size ?? "xs",
5044
+ disabled: props.disabled,
5045
+ style: props.style,
5046
+ data,
5047
+ value: currentValue ?? null,
5048
+ searchable: true,
5049
+ onChange: (v) => {
5050
+ const name = v ?? "";
5051
+ if (inCompound) {
5052
+ ctx.onChange({ name: name || void 0, grade: void 0 });
5053
+ } else if (props.onChange) {
5054
+ props.onChange(name);
5055
+ }
5056
+ },
5057
+ allowDeselect: false
5058
+ }
5059
+ );
5060
+ }
5061
+ function GroupRank(props) {
5062
+ const ctx = React6.useContext(GroupSelectContext);
5063
+ if (ctx === null) {
5064
+ throw new Error("<GroupRank> must be a child of <GroupSelect>");
5065
+ }
5066
+ const jobs = useFrameworkGroups((s) => s.jobs);
5067
+ const gangs = useFrameworkGroups((s) => s.gangs);
5068
+ const all = [...jobs, ...gangs];
5069
+ const selectedGroup = all.find((g) => g.name === ctx.value.name) ?? null;
5070
+ const grades = selectedGroup?.grades ?? [];
5071
+ const data = grades.map((g) => ({
5072
+ value: String(g.grade),
5073
+ label: `${g.grade} \u2014 ${g.label || g.name}${g.isBoss ? " (boss)" : ""}`
5074
+ }));
5075
+ return /* @__PURE__ */ jsxRuntime.jsx(
5076
+ core.Select,
5077
+ {
5078
+ label: props.label,
5079
+ description: props.description,
5080
+ placeholder: props.placeholder ?? (locale("SelectGrade") || "Select grade\u2026"),
5081
+ size: props.size ?? "xs",
5082
+ style: props.style,
5083
+ data,
5084
+ value: ctx.value.grade != null ? String(ctx.value.grade) : null,
5085
+ onChange: (v) => {
5086
+ ctx.onChange({
5087
+ ...ctx.value,
5088
+ grade: v != null ? Number(v) : void 0
5089
+ });
5090
+ },
5091
+ disabled: !selectedGroup || grades.length === 0,
5092
+ allowDeselect: false
5093
+ }
5094
+ );
5095
+ }
5096
+ GroupSelect.Name = GroupName;
5097
+ GroupSelect.Rank = GroupRank;
5098
+ var KeyBindContext = React6.createContext(null);
4756
5099
  function useKeyBindContext() {
4757
- const ctx = React5.useContext(KeyBindContext);
5100
+ const ctx = React6.useContext(KeyBindContext);
4758
5101
  if (!ctx) {
4759
5102
  throw new Error("FiveMKeyBindInput.* must be used inside <FiveMKeyBindInput>");
4760
5103
  }
@@ -4833,7 +5176,7 @@ function AsyncSaveButton({
4833
5176
  style
4834
5177
  }) {
4835
5178
  const theme2 = core.useMantineTheme();
4836
- const [state, setState] = React5.useState("idle");
5179
+ const [state, setState] = React6.useState("idle");
4837
5180
  const handleClick = async () => {
4838
5181
  if (state === "pending") return;
4839
5182
  setState("pending");
@@ -4900,10 +5243,10 @@ function TestBed({
4900
5243
  disablePersistence = false,
4901
5244
  title = "TestBed"
4902
5245
  }) {
4903
- const [open, setOpen] = React5.useState(false);
4904
- const itemsRef = React5.useRef(items);
5246
+ const [open, setOpen] = React6.useState(false);
5247
+ const itemsRef = React6.useRef(items);
4905
5248
  itemsRef.current = items;
4906
- React5.useEffect(() => {
5249
+ React6.useEffect(() => {
4907
5250
  if (!isEnvBrowser() || disablePersistence) return;
4908
5251
  const persisted = loadPersistedState(storageKey);
4909
5252
  itemsRef.current.forEach((item) => {
@@ -5229,7 +5572,7 @@ function mergeMantineThemeSafe(base, custom, override) {
5229
5572
  }
5230
5573
  };
5231
5574
  }
5232
- var DirkErrorBoundary = class extends React5__default.default.Component {
5575
+ var DirkErrorBoundary = class extends React6__default.default.Component {
5233
5576
  constructor() {
5234
5577
  super(...arguments);
5235
5578
  __publicField(this, "state", { error: null, stack: void 0 });
@@ -5293,12 +5636,12 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
5293
5636
  game
5294
5637
  } = useSettings();
5295
5638
  localeStore((s) => s.locales);
5296
- React5.useLayoutEffect(() => {
5639
+ React6.useLayoutEffect(() => {
5297
5640
  useSettings.setState({
5298
5641
  overideResourceName
5299
5642
  });
5300
5643
  }, [overideResourceName]);
5301
- React5.useEffect(() => {
5644
+ React6.useEffect(() => {
5302
5645
  fetchNui("NUI_READY").catch(() => {
5303
5646
  });
5304
5647
  Promise.all([
@@ -5317,7 +5660,7 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
5317
5660
  if (!data || typeof data !== "object") return;
5318
5661
  useSettings.setState(data);
5319
5662
  });
5320
- const mergedTheme = React5.useMemo(
5663
+ const mergedTheme = React6.useMemo(
5321
5664
  () => mergeMantineThemeSafe(
5322
5665
  { ...theme_default, primaryColor, primaryShade },
5323
5666
  customTheme,
@@ -5325,7 +5668,7 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
5325
5668
  ),
5326
5669
  [primaryColor, primaryShade, customTheme, themeOverride]
5327
5670
  );
5328
- React5.useEffect(() => {
5671
+ React6.useEffect(() => {
5329
5672
  document.body.style.fontFamily = game === "rdr3" ? '"Red Dead", sans-serif' : '"Akrobat Regular", sans-serif';
5330
5673
  }, [game]);
5331
5674
  const content = isEnvBrowser() ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -5343,6 +5686,7 @@ function DirkProvider({ children, overideResourceName, themeOverride }) {
5343
5686
  exports.AdminPageTitle = AdminPageTitle;
5344
5687
  exports.AsyncSaveButton = AsyncSaveButton;
5345
5688
  exports.BlipColorSelect = BlipColorSelect;
5689
+ exports.BlipDisplaySelect = BlipDisplaySelect;
5346
5690
  exports.BlipIconSelect = BlipIconSelect;
5347
5691
  exports.BorderedIcon = BorderedIcon;
5348
5692
  exports.ConfigPanel = ConfigPanel;
@@ -5352,6 +5696,9 @@ exports.DirkProvider = DirkProvider;
5352
5696
  exports.FiveMKeyBindInput = FiveMKeyBindInput;
5353
5697
  exports.FloatingParticles = FloatingParticles;
5354
5698
  exports.FormProvider = FormProvider;
5699
+ exports.GroupName = GroupName;
5700
+ exports.GroupRank = GroupRank;
5701
+ exports.GroupSelect = GroupSelect;
5355
5702
  exports.INPUT_MAPPER_KEYS_BY_PRIMARY = INPUT_MAPPER_KEYS_BY_PRIMARY;
5356
5703
  exports.INPUT_MAPPER_PRIMARY_OPTIONS = INPUT_MAPPER_PRIMARY_OPTIONS;
5357
5704
  exports.InfoBox = InfoBox;
@@ -5368,6 +5715,7 @@ exports.MotionText = MotionText;
5368
5715
  exports.NavBar = NavBar;
5369
5716
  exports.NavigationContext = NavigationContext;
5370
5717
  exports.NavigationProvider = NavigationProvider;
5718
+ exports.PositionPicker = PositionPicker;
5371
5719
  exports.PromptModal = PromptModal;
5372
5720
  exports.SegmentedControl = SegmentedControl;
5373
5721
  exports.SegmentedProgress = SegmentedProgress;
@@ -5402,6 +5750,7 @@ exports.openLink = openLink;
5402
5750
  exports.registerInitialFetch = registerInitialFetch;
5403
5751
  exports.registerInitialLuaTableFetch = registerInitialLuaTableFetch;
5404
5752
  exports.runFetches = runFetches;
5753
+ exports.selectAllGroups = selectAllGroups;
5405
5754
  exports.splitFAString = splitFAString;
5406
5755
  exports.updatePresignedURL = updatePresignedURL;
5407
5756
  exports.uploadImage = uploadImage;
@@ -5413,6 +5762,7 @@ exports.useFormError = useFormError;
5413
5762
  exports.useFormErrors = useFormErrors;
5414
5763
  exports.useFormField = useFormField;
5415
5764
  exports.useFormFields = useFormFields;
5765
+ exports.useFrameworkGroups = useFrameworkGroups;
5416
5766
  exports.useItems = useItems;
5417
5767
  exports.useItemsList = useItemsList;
5418
5768
  exports.useModal = useModal;