dirk-cfx-react 1.1.87 → 1.1.89
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/components/index.cjs +872 -32
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +192 -2
- package/dist/components/index.d.ts +192 -2
- package/dist/components/index.js +863 -37
- package/dist/components/index.js.map +1 -1
- package/dist/hooks/index.cjs +159 -26
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +16 -1
- package/dist/hooks/index.d.ts +16 -1
- package/dist/hooks/index.js +157 -28
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +1439 -451
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1342 -374
- package/dist/index.js.map +1 -1
- package/dist/providers/index.cjs +133 -1
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +3 -1
- package/dist/providers/index.d.ts +3 -1
- package/dist/providers/index.js +134 -3
- package/dist/providers/index.js.map +1 -1
- package/dist/usePlayers-BlGBBevs.d.cts +37 -0
- package/dist/usePlayers-BlGBBevs.d.ts +37 -0
- package/dist/utils/index.cjs +16 -8
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.cts +2 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +16 -9
- package/dist/utils/index.js.map +1 -1
- package/package.json +123 -117
package/dist/components/index.js
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { Flex, Text, Image, TextInput, Select, Box, useMantineTheme, Tooltip, alpha, Progress, RingProgress, Portal, Button, NumberInput, MultiSelect, Loader, Switch, ActionIcon, Stack, ColorInput, Popover, Group, JsonInput } from '@mantine/core';
|
|
2
|
-
import {
|
|
3
|
-
import { createContext,
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { createContext, memo, useRef, useEffect, useContext, useState, useCallback, useMemo } from 'react';
|
|
4
4
|
import { create, useStore, createStore } from 'zustand';
|
|
5
5
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
6
6
|
import { motion, AnimatePresence, useMotionValue } from 'framer-motion';
|
|
7
|
-
import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
|
|
7
|
+
import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, DoorOpen, Plus, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
|
|
8
8
|
import clickSoundUrl from '../click_sound-PNCRRTM4.mp3';
|
|
9
9
|
import hoverSoundUrl from '../hover_sound-NBUA222C.mp3';
|
|
10
10
|
import { notifications } from '@mantine/notifications';
|
|
11
|
-
import { QueryClient,
|
|
11
|
+
import { QueryClient, useQuery, keepPreviousData, useInfiniteQuery } from '@tanstack/react-query';
|
|
12
|
+
import '@mantine/core/styles.css';
|
|
13
|
+
import '@mantine/notifications/styles.css';
|
|
14
|
+
import './styles/fonts.css';
|
|
15
|
+
import './styles/notify.css';
|
|
16
|
+
import './styles/scrollBar.css';
|
|
17
|
+
import './styles/tornEdge.css';
|
|
18
|
+
import { library } from '@fortawesome/fontawesome-svg-core';
|
|
19
|
+
import { fab } from '@fortawesome/free-brands-svg-icons';
|
|
20
|
+
import { far } from '@fortawesome/free-regular-svg-icons';
|
|
21
|
+
import { fas } from '@fortawesome/free-solid-svg-icons';
|
|
12
22
|
import { generateColors } from '@mantine/colors-generator';
|
|
23
|
+
import { createPortal } from 'react-dom';
|
|
24
|
+
import { CRS, tileLayer, latLngBounds, latLng } from 'leaflet';
|
|
25
|
+
import 'leaflet/dist/leaflet.css';
|
|
26
|
+
import { MapContainer, useMap } from 'react-leaflet';
|
|
27
|
+
import { Marker } from '@adamscybot/react-leaflet-component-marker';
|
|
13
28
|
|
|
14
29
|
// src/components/BlipSelect.tsx
|
|
15
30
|
var BLIP_ENTRIES = [
|
|
@@ -953,6 +968,18 @@ var BLIP_COLOR_DATA = BLIP_COLORS.map(([id, label]) => ({
|
|
|
953
968
|
}));
|
|
954
969
|
var blipEntryMap = new Map(BLIP_ENTRIES.map(([id, name, ext]) => [String(id), { id, name, ext }]));
|
|
955
970
|
var blipColorMap = new Map(BLIP_COLORS.map(([id, label, hex]) => [String(id), { id, label, hex }]));
|
|
971
|
+
function getBlipEntry(spriteId) {
|
|
972
|
+
if (spriteId == null) return void 0;
|
|
973
|
+
return blipEntryMap.get(String(spriteId));
|
|
974
|
+
}
|
|
975
|
+
function getBlipColor(colorId) {
|
|
976
|
+
if (colorId == null) return void 0;
|
|
977
|
+
return blipColorMap.get(String(colorId));
|
|
978
|
+
}
|
|
979
|
+
function blipUrlForSprite(spriteId) {
|
|
980
|
+
const entry = getBlipEntry(spriteId);
|
|
981
|
+
return entry ? blipUrl(entry.name, entry.ext) : null;
|
|
982
|
+
}
|
|
956
983
|
var renderBlipOption = ({ option }) => {
|
|
957
984
|
const entry = blipEntryMap.get(option.value);
|
|
958
985
|
if (!entry) return option.label;
|
|
@@ -1230,11 +1257,11 @@ var colorNames = {
|
|
|
1230
1257
|
Yellow: { r: 255, g: 255, b: 0 },
|
|
1231
1258
|
YellowGreen: { r: 154, g: 205, b: 50 }
|
|
1232
1259
|
};
|
|
1233
|
-
function colorWithAlpha(color,
|
|
1260
|
+
function colorWithAlpha(color, alpha17) {
|
|
1234
1261
|
const lowerCasedColor = color.toLowerCase();
|
|
1235
1262
|
if (colorNames[lowerCasedColor]) {
|
|
1236
1263
|
const rgb = colorNames[lowerCasedColor];
|
|
1237
|
-
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
|
|
1264
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha17})`;
|
|
1238
1265
|
}
|
|
1239
1266
|
if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
|
|
1240
1267
|
const hex = color.slice(1);
|
|
@@ -1242,12 +1269,12 @@ function colorWithAlpha(color, alpha12) {
|
|
|
1242
1269
|
const r = bigint >> 16 & 255;
|
|
1243
1270
|
const g = bigint >> 8 & 255;
|
|
1244
1271
|
const b = bigint & 255;
|
|
1245
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
1272
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha17})`;
|
|
1246
1273
|
}
|
|
1247
1274
|
if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
|
|
1248
1275
|
const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
|
|
1249
1276
|
if (result) {
|
|
1250
|
-
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${
|
|
1277
|
+
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha17})`;
|
|
1251
1278
|
}
|
|
1252
1279
|
}
|
|
1253
1280
|
return color;
|
|
@@ -1342,9 +1369,22 @@ if (typeof window !== "undefined") {
|
|
|
1342
1369
|
const msg = event.data;
|
|
1343
1370
|
if (!msg || msg.action !== "UPDATE_DIRK_LIB_LOCALES") return;
|
|
1344
1371
|
if (!msg.data || typeof msg.data !== "object") return;
|
|
1372
|
+
if (Object.keys(msg.data).length === 0) return;
|
|
1345
1373
|
localeStore.setState({ locales: msg.data });
|
|
1346
1374
|
});
|
|
1347
1375
|
}
|
|
1376
|
+
|
|
1377
|
+
// src/utils/map.ts
|
|
1378
|
+
var mapCenter = [-119.43, 58.84];
|
|
1379
|
+
var latPr100 = 1.421;
|
|
1380
|
+
function gameToMap(x, y) {
|
|
1381
|
+
return [
|
|
1382
|
+
mapCenter[0] + latPr100 / 100 * y,
|
|
1383
|
+
// lng
|
|
1384
|
+
mapCenter[1] + latPr100 / 100 * x
|
|
1385
|
+
// lat
|
|
1386
|
+
];
|
|
1387
|
+
}
|
|
1348
1388
|
var useItems = create(() => ({}));
|
|
1349
1389
|
var useItemsList = (excludeItemNames = []) => {
|
|
1350
1390
|
const excludeSet = new Set(excludeItemNames);
|
|
@@ -1364,15 +1404,21 @@ var useFrameworkGroups = create(() => ({
|
|
|
1364
1404
|
gangs: [],
|
|
1365
1405
|
loaded: false
|
|
1366
1406
|
}));
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1407
|
+
var frameworkGroupsRequested = false;
|
|
1408
|
+
function ensureFrameworkGroups() {
|
|
1409
|
+
if (frameworkGroupsRequested) return;
|
|
1410
|
+
frameworkGroupsRequested = true;
|
|
1411
|
+
fetchNui("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
|
|
1412
|
+
useFrameworkGroups.setState({
|
|
1413
|
+
jobs: Array.isArray(data?.jobs) ? data.jobs : [],
|
|
1414
|
+
gangs: Array.isArray(data?.gangs) ? data.gangs : [],
|
|
1415
|
+
loaded: true
|
|
1416
|
+
});
|
|
1417
|
+
}).catch(() => {
|
|
1418
|
+
frameworkGroupsRequested = false;
|
|
1419
|
+
useFrameworkGroups.setState({ loaded: true });
|
|
1372
1420
|
});
|
|
1373
|
-
}
|
|
1374
|
-
useFrameworkGroups.setState({ loaded: true });
|
|
1375
|
-
});
|
|
1421
|
+
}
|
|
1376
1422
|
|
|
1377
1423
|
// src/utils/inputMapper.ts
|
|
1378
1424
|
var INPUT_MAPPER_PRIMARY_OPTIONS = [
|
|
@@ -2381,7 +2427,7 @@ function NavigationProvider({ children, defaultPage }) {
|
|
|
2381
2427
|
}
|
|
2382
2428
|
function NavBar(props) {
|
|
2383
2429
|
const pageId = useNavigation((state) => state.pageId);
|
|
2384
|
-
const
|
|
2430
|
+
const store2 = useNavigationStore();
|
|
2385
2431
|
return /* @__PURE__ */ jsx(
|
|
2386
2432
|
SegmentedControl,
|
|
2387
2433
|
{
|
|
@@ -2390,7 +2436,7 @@ function NavBar(props) {
|
|
|
2390
2436
|
value: pageId,
|
|
2391
2437
|
items: props.items,
|
|
2392
2438
|
onChange: (value) => {
|
|
2393
|
-
|
|
2439
|
+
store2.setState({ pageId: value });
|
|
2394
2440
|
}
|
|
2395
2441
|
}
|
|
2396
2442
|
);
|
|
@@ -3697,34 +3743,82 @@ function FormProvider({
|
|
|
3697
3743
|
return /* @__PURE__ */ jsx(FormContext.Provider, { value: storeRef.current, children });
|
|
3698
3744
|
}
|
|
3699
3745
|
function useForm() {
|
|
3700
|
-
const
|
|
3701
|
-
if (!
|
|
3746
|
+
const store2 = useContext(FormContext);
|
|
3747
|
+
if (!store2) {
|
|
3702
3748
|
throw new Error("useForm must be used inside <FormProvider>");
|
|
3703
3749
|
}
|
|
3704
|
-
const state = useStore(
|
|
3750
|
+
const state = useStore(store2);
|
|
3705
3751
|
const changedFields = useMemo(() => {
|
|
3706
3752
|
return collectChangedPaths(state.values, state.initialValues);
|
|
3707
3753
|
}, [state.values, state.initialValues]);
|
|
3708
3754
|
return { ...state, changedFields, changedCount: changedFields.length };
|
|
3709
3755
|
}
|
|
3710
3756
|
function useFormField(path) {
|
|
3711
|
-
const
|
|
3712
|
-
if (!
|
|
3757
|
+
const store2 = useContext(FormContext);
|
|
3758
|
+
if (!store2) {
|
|
3713
3759
|
throw new Error("useFormField must be used inside <FormProvider>");
|
|
3714
3760
|
}
|
|
3715
|
-
return useStore(
|
|
3761
|
+
return useStore(store2, (s) => getNested(s.values, path));
|
|
3716
3762
|
}
|
|
3717
3763
|
function useFormActions() {
|
|
3718
|
-
const
|
|
3719
|
-
if (!
|
|
3764
|
+
const store2 = useContext(FormContext);
|
|
3765
|
+
if (!store2) {
|
|
3720
3766
|
throw new Error("useFormActions must be used inside <FormProvider>");
|
|
3721
3767
|
}
|
|
3722
|
-
return
|
|
3768
|
+
return store2.getState();
|
|
3769
|
+
}
|
|
3770
|
+
var store = /* @__PURE__ */ new Map();
|
|
3771
|
+
var listeners = /* @__PURE__ */ new Map();
|
|
3772
|
+
function notify(key) {
|
|
3773
|
+
const ls = listeners.get(key);
|
|
3774
|
+
if (!ls) return;
|
|
3775
|
+
ls.forEach((fn) => fn());
|
|
3776
|
+
}
|
|
3777
|
+
function useAdminState(key, initial) {
|
|
3778
|
+
const [, setTick] = useState(0);
|
|
3779
|
+
useEffect(() => {
|
|
3780
|
+
const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
3781
|
+
listeners.set(key, set);
|
|
3782
|
+
const handler = () => setTick((t3) => t3 + 1);
|
|
3783
|
+
set.add(handler);
|
|
3784
|
+
return () => {
|
|
3785
|
+
set.delete(handler);
|
|
3786
|
+
if (set.size === 0) listeners.delete(key);
|
|
3787
|
+
};
|
|
3788
|
+
}, [key]);
|
|
3789
|
+
const value = store.has(key) ? store.get(key) : initial;
|
|
3790
|
+
const setValue = (v) => {
|
|
3791
|
+
const next = typeof v === "function" ? v(value) : v;
|
|
3792
|
+
store.set(key, next);
|
|
3793
|
+
notify(key);
|
|
3794
|
+
};
|
|
3795
|
+
return [value, setValue];
|
|
3723
3796
|
}
|
|
3724
3797
|
function getScriptConfigInstance() {
|
|
3725
3798
|
throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
|
|
3726
3799
|
}
|
|
3727
|
-
var
|
|
3800
|
+
var useAdminToolStore = create((set, get) => ({
|
|
3801
|
+
active: null,
|
|
3802
|
+
begin: (spec) => new Promise((resolve) => {
|
|
3803
|
+
const prev = get().active;
|
|
3804
|
+
if (prev) prev.resolve(null);
|
|
3805
|
+
set({ active: { ...spec, resolve } });
|
|
3806
|
+
}),
|
|
3807
|
+
resolveActive: (value) => {
|
|
3808
|
+
const cur = get().active;
|
|
3809
|
+
if (!cur) return;
|
|
3810
|
+
cur.resolve(value);
|
|
3811
|
+
set({ active: null });
|
|
3812
|
+
},
|
|
3813
|
+
cancelActive: () => {
|
|
3814
|
+
const cur = get().active;
|
|
3815
|
+
if (!cur) return;
|
|
3816
|
+
cur.resolve(null);
|
|
3817
|
+
set({ active: null });
|
|
3818
|
+
}
|
|
3819
|
+
}));
|
|
3820
|
+
library.add(fas, far, fab);
|
|
3821
|
+
var dirkQueryClient = new QueryClient({
|
|
3728
3822
|
defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
|
|
3729
3823
|
});
|
|
3730
3824
|
function NavItemButton({
|
|
@@ -4055,7 +4149,7 @@ function ConfigPanelInner({
|
|
|
4055
4149
|
const theme = useMantineTheme();
|
|
4056
4150
|
const color = theme.colors[theme.primaryColor][5];
|
|
4057
4151
|
const version = useSettings((s) => s.resourceVersion);
|
|
4058
|
-
const [activeTab, setActiveTab] =
|
|
4152
|
+
const [activeTab, setActiveTab] = useAdminState("__dirkConfigPanel:activeTab", navItems[0]?.id ?? "");
|
|
4059
4153
|
const firstMountRef = useRef(true);
|
|
4060
4154
|
const [jsonOpen, setJsonOpen] = useState(false);
|
|
4061
4155
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
@@ -4098,8 +4192,8 @@ function ConfigPanelInner({
|
|
|
4098
4192
|
setResetOpen(false);
|
|
4099
4193
|
const result = await resetConfig();
|
|
4100
4194
|
if (result?.success) {
|
|
4101
|
-
const { store } = getScriptConfigInstance();
|
|
4102
|
-
form.reinitialize(cloneConfig(
|
|
4195
|
+
const { store: store2 } = getScriptConfigInstance();
|
|
4196
|
+
form.reinitialize(cloneConfig(store2.getState()));
|
|
4103
4197
|
notifications.show({
|
|
4104
4198
|
color: "green",
|
|
4105
4199
|
title: locale("ConfigResetSuccessTitle"),
|
|
@@ -4327,13 +4421,13 @@ function ServerOnlyFetcher() {
|
|
|
4327
4421
|
var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
|
|
4328
4422
|
function ConfigPanel(props) {
|
|
4329
4423
|
const { open, onClose = defaultOnClose } = props;
|
|
4330
|
-
const { store, updateConfig } = getScriptConfigInstance();
|
|
4424
|
+
const { store: store2, updateConfig } = getScriptConfigInstance();
|
|
4331
4425
|
const [isSaving, setIsSaving] = useState(false);
|
|
4332
4426
|
if (!open) return null;
|
|
4333
|
-
return /* @__PURE__ */ jsx(
|
|
4427
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
|
|
4334
4428
|
FormProvider,
|
|
4335
4429
|
{
|
|
4336
|
-
initialValues: cloneConfig(
|
|
4430
|
+
initialValues: cloneConfig(store2.getState()),
|
|
4337
4431
|
onSubmit: async (form) => {
|
|
4338
4432
|
if (isSaving) return;
|
|
4339
4433
|
setIsSaving(true);
|
|
@@ -4341,7 +4435,7 @@ function ConfigPanel(props) {
|
|
|
4341
4435
|
const result = await updateConfig(form.values);
|
|
4342
4436
|
if (result?.success) {
|
|
4343
4437
|
form.reinitialize(cloneConfig(form.values));
|
|
4344
|
-
|
|
4438
|
+
dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
|
|
4345
4439
|
useMissingItemsAudit.getState().refresh();
|
|
4346
4440
|
notifications.show({
|
|
4347
4441
|
color: "green",
|
|
@@ -4351,7 +4445,7 @@ function ConfigPanel(props) {
|
|
|
4351
4445
|
});
|
|
4352
4446
|
return;
|
|
4353
4447
|
}
|
|
4354
|
-
form.reinitialize(cloneConfig(
|
|
4448
|
+
form.reinitialize(cloneConfig(store2.getState()));
|
|
4355
4449
|
const err = result?._error || "Unknown";
|
|
4356
4450
|
console.warn(`[ConfigPanel] config save failed: ${err}`);
|
|
4357
4451
|
const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
|
|
@@ -4848,6 +4942,9 @@ function GroupName(props) {
|
|
|
4848
4942
|
const ctx = useContext(GroupSelectContext);
|
|
4849
4943
|
const jobs = useFrameworkGroups((s) => s.jobs);
|
|
4850
4944
|
const gangs = useFrameworkGroups((s) => s.gangs);
|
|
4945
|
+
useEffect(() => {
|
|
4946
|
+
ensureFrameworkGroups();
|
|
4947
|
+
}, []);
|
|
4851
4948
|
const inCompound = ctx !== null;
|
|
4852
4949
|
const currentValue = inCompound ? ctx.value.name : props.value;
|
|
4853
4950
|
const filterType = inCompound ? ctx.type : props.type;
|
|
@@ -4887,6 +4984,9 @@ function GroupRank(props) {
|
|
|
4887
4984
|
}
|
|
4888
4985
|
const jobs = useFrameworkGroups((s) => s.jobs);
|
|
4889
4986
|
const gangs = useFrameworkGroups((s) => s.gangs);
|
|
4987
|
+
useEffect(() => {
|
|
4988
|
+
ensureFrameworkGroups();
|
|
4989
|
+
}, []);
|
|
4890
4990
|
const all = [...jobs, ...gangs];
|
|
4891
4991
|
const selectedGroup = all.find((g) => g.name === ctx.value.name) ?? null;
|
|
4892
4992
|
const grades = selectedGroup?.grades ?? [];
|
|
@@ -6532,7 +6632,733 @@ function AccountSelect(props) {
|
|
|
6532
6632
|
!hideFrameworkHint && /* @__PURE__ */ jsx(FrameworkHint, { framework })
|
|
6533
6633
|
] });
|
|
6534
6634
|
}
|
|
6635
|
+
var BODY_HIDE_STYLE_ID = "dirk-instruction-panel-style";
|
|
6636
|
+
var BODY_HIDE_ATTR = "data-dirk-instruction-active";
|
|
6637
|
+
var OVERLAY_ATTR = "data-dirk-instruction-overlay";
|
|
6638
|
+
function ensureBodyHideStyle() {
|
|
6639
|
+
if (document.getElementById(BODY_HIDE_STYLE_ID)) return;
|
|
6640
|
+
const el = document.createElement("style");
|
|
6641
|
+
el.id = BODY_HIDE_STYLE_ID;
|
|
6642
|
+
el.textContent = `
|
|
6643
|
+
body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {
|
|
6644
|
+
visibility: hidden !important;
|
|
6645
|
+
opacity: 0 !important;
|
|
6646
|
+
pointer-events: none !important;
|
|
6647
|
+
}
|
|
6648
|
+
`;
|
|
6649
|
+
document.head.appendChild(el);
|
|
6650
|
+
}
|
|
6651
|
+
function InstructionPanel({
|
|
6652
|
+
visible,
|
|
6653
|
+
title,
|
|
6654
|
+
hint,
|
|
6655
|
+
keys,
|
|
6656
|
+
icon: Icon = MapPin,
|
|
6657
|
+
hideRestOfAdmin = true
|
|
6658
|
+
}) {
|
|
6659
|
+
const theme = useMantineTheme();
|
|
6660
|
+
const pc = theme.colors[theme.primaryColor];
|
|
6661
|
+
useEffect(() => {
|
|
6662
|
+
if (!visible || !hideRestOfAdmin) return;
|
|
6663
|
+
ensureBodyHideStyle();
|
|
6664
|
+
document.body.setAttribute(BODY_HIDE_ATTR, "");
|
|
6665
|
+
return () => {
|
|
6666
|
+
document.body.removeAttribute(BODY_HIDE_ATTR);
|
|
6667
|
+
};
|
|
6668
|
+
}, [visible, hideRestOfAdmin]);
|
|
6669
|
+
if (!visible) return null;
|
|
6670
|
+
return createPortal(
|
|
6671
|
+
/* @__PURE__ */ jsx(
|
|
6672
|
+
"div",
|
|
6673
|
+
{
|
|
6674
|
+
...{ [OVERLAY_ATTR]: "" },
|
|
6675
|
+
style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: 1e4 },
|
|
6676
|
+
children: /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
|
|
6677
|
+
motion.div,
|
|
6678
|
+
{
|
|
6679
|
+
initial: { opacity: 0, y: 12, scale: 0.92 },
|
|
6680
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
6681
|
+
exit: { opacity: 0, y: 12, scale: 0.92 },
|
|
6682
|
+
transition: { duration: 0.25, ease: "easeInOut" },
|
|
6683
|
+
style: { position: "absolute", bottom: "3vh", right: "3vh", userSelect: "none" },
|
|
6684
|
+
children: /* @__PURE__ */ jsxs(
|
|
6685
|
+
Flex,
|
|
6686
|
+
{
|
|
6687
|
+
direction: "column",
|
|
6688
|
+
gap: "0.8vh",
|
|
6689
|
+
style: {
|
|
6690
|
+
background: alpha(theme.colors.dark[9], 0.55),
|
|
6691
|
+
border: "0.1vh solid rgba(255,255,255,0.07)",
|
|
6692
|
+
borderRadius: theme.radius.sm,
|
|
6693
|
+
boxShadow: "0 0.74vh 2.96vh rgba(0,0,0,0.5)",
|
|
6694
|
+
padding: "1.4vh 1.6vh",
|
|
6695
|
+
minWidth: "22vh",
|
|
6696
|
+
maxWidth: "28vh"
|
|
6697
|
+
},
|
|
6698
|
+
children: [
|
|
6699
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: "0.6vh", children: [
|
|
6700
|
+
/* @__PURE__ */ jsx(Icon, { size: "1.6vh", color: pc[6], strokeWidth: 2.5 }),
|
|
6701
|
+
/* @__PURE__ */ jsx(
|
|
6702
|
+
Text,
|
|
6703
|
+
{
|
|
6704
|
+
style: {
|
|
6705
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6706
|
+
fontSize: "1.7vh",
|
|
6707
|
+
fontWeight: 700,
|
|
6708
|
+
letterSpacing: "0.14em",
|
|
6709
|
+
textTransform: "uppercase",
|
|
6710
|
+
color: pc[6],
|
|
6711
|
+
textShadow: `0 0 0.8vh ${alpha(pc[7], 0.5)}, 0 0 1.6vh ${alpha(pc[9], 0.3)}`
|
|
6712
|
+
},
|
|
6713
|
+
children: title
|
|
6714
|
+
}
|
|
6715
|
+
)
|
|
6716
|
+
] }),
|
|
6717
|
+
(hint || keys && keys.length > 0) && /* @__PURE__ */ jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
|
|
6718
|
+
hint && /* @__PURE__ */ jsx(
|
|
6719
|
+
Text,
|
|
6720
|
+
{
|
|
6721
|
+
style: {
|
|
6722
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6723
|
+
fontSize: "1.05vh",
|
|
6724
|
+
color: "rgba(255,255,255,0.45)",
|
|
6725
|
+
letterSpacing: "0.06em",
|
|
6726
|
+
textTransform: "uppercase",
|
|
6727
|
+
lineHeight: 1.4
|
|
6728
|
+
},
|
|
6729
|
+
children: hint
|
|
6730
|
+
}
|
|
6731
|
+
),
|
|
6732
|
+
keys && keys.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6733
|
+
hint && /* @__PURE__ */ jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
|
|
6734
|
+
/* @__PURE__ */ jsx(Flex, { direction: "column", gap: "0.5vh", children: keys.map((k, i) => /* @__PURE__ */ jsx(InstructionKeyRow, { keyLabel: k.key, action: k.action }, i)) })
|
|
6735
|
+
] })
|
|
6736
|
+
]
|
|
6737
|
+
}
|
|
6738
|
+
)
|
|
6739
|
+
},
|
|
6740
|
+
"instruction-card"
|
|
6741
|
+
) })
|
|
6742
|
+
}
|
|
6743
|
+
),
|
|
6744
|
+
document.body
|
|
6745
|
+
);
|
|
6746
|
+
}
|
|
6747
|
+
function renderKeyContent(keyLabel) {
|
|
6748
|
+
const normalized = keyLabel.trim().toUpperCase();
|
|
6749
|
+
if (normalized === "LMB") return /* @__PURE__ */ jsx(MouseIcon, { side: "left" });
|
|
6750
|
+
if (normalized === "RMB") return /* @__PURE__ */ jsx(MouseIcon, { side: "right" });
|
|
6751
|
+
if (normalized === "BACKSPACE" || keyLabel === "\u232B") return /* @__PURE__ */ jsx(BackspaceIcon, {});
|
|
6752
|
+
return /* @__PURE__ */ jsx(
|
|
6753
|
+
Text,
|
|
6754
|
+
{
|
|
6755
|
+
style: {
|
|
6756
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6757
|
+
fontSize: "1.2vh",
|
|
6758
|
+
color: "rgba(255,255,255,0.85)",
|
|
6759
|
+
lineHeight: 1,
|
|
6760
|
+
padding: "0 0.3vh"
|
|
6761
|
+
},
|
|
6762
|
+
children: keyLabel
|
|
6763
|
+
}
|
|
6764
|
+
);
|
|
6765
|
+
}
|
|
6766
|
+
function InstructionKeyRow({ keyLabel, action }) {
|
|
6767
|
+
const normalized = keyLabel.trim().toUpperCase();
|
|
6768
|
+
const isIconKey = normalized === "LMB" || normalized === "RMB" || normalized === "BACKSPACE" || keyLabel === "\u232B";
|
|
6769
|
+
const minWidth = isIconKey ? "2.6vh" : "2.4vh";
|
|
6770
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "0.6vh", children: [
|
|
6771
|
+
/* @__PURE__ */ jsx(
|
|
6772
|
+
"div",
|
|
6773
|
+
{
|
|
6774
|
+
style: {
|
|
6775
|
+
minWidth,
|
|
6776
|
+
height: "2.4vh",
|
|
6777
|
+
padding: "0 0.4vh",
|
|
6778
|
+
borderRadius: "0.3vh",
|
|
6779
|
+
border: "0.15vh solid rgba(255,255,255,0.35)",
|
|
6780
|
+
background: "rgba(255,255,255,0.06)",
|
|
6781
|
+
display: "flex",
|
|
6782
|
+
alignItems: "center",
|
|
6783
|
+
justifyContent: "center",
|
|
6784
|
+
opacity: 0.85,
|
|
6785
|
+
filter: "drop-shadow(0 0 0.3vh rgba(0,0,0,0.5))",
|
|
6786
|
+
flexShrink: 0,
|
|
6787
|
+
boxSizing: "border-box"
|
|
6788
|
+
},
|
|
6789
|
+
children: renderKeyContent(keyLabel)
|
|
6790
|
+
}
|
|
6791
|
+
),
|
|
6792
|
+
/* @__PURE__ */ jsx(
|
|
6793
|
+
Text,
|
|
6794
|
+
{
|
|
6795
|
+
style: {
|
|
6796
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6797
|
+
fontSize: "1.05vh",
|
|
6798
|
+
color: "rgba(255,255,255,0.45)",
|
|
6799
|
+
letterSpacing: "0.06em",
|
|
6800
|
+
textTransform: "uppercase"
|
|
6801
|
+
},
|
|
6802
|
+
children: action
|
|
6803
|
+
}
|
|
6804
|
+
)
|
|
6805
|
+
] });
|
|
6806
|
+
}
|
|
6807
|
+
function MouseIcon({ side }) {
|
|
6808
|
+
const stroke = "rgba(255,255,255,0.85)";
|
|
6809
|
+
const fillActive = "rgba(255,255,255,0.85)";
|
|
6810
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 22", width: "1.4vh", height: "1.9vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", children: [
|
|
6811
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "14", height: "20", rx: "6" }),
|
|
6812
|
+
/* @__PURE__ */ jsx("line", { x1: "8", y1: "1", x2: "8", y2: "9" }),
|
|
6813
|
+
/* @__PURE__ */ jsx(
|
|
6814
|
+
"path",
|
|
6815
|
+
{
|
|
6816
|
+
d: side === "left" ? "M 7.4 1.6 L 2 1.6 A 5 5 0 0 0 1 6 L 1 9 L 7.4 9 Z" : "M 8.6 1.6 L 14 1.6 A 5 5 0 0 1 15 6 L 15 9 L 8.6 9 Z",
|
|
6817
|
+
fill: fillActive,
|
|
6818
|
+
stroke: "none"
|
|
6819
|
+
}
|
|
6820
|
+
)
|
|
6821
|
+
] });
|
|
6822
|
+
}
|
|
6823
|
+
function BackspaceIcon() {
|
|
6824
|
+
const stroke = "rgba(255,255,255,0.85)";
|
|
6825
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 22 16", width: "1.7vh", height: "1.3vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", strokeLinecap: "round", children: [
|
|
6826
|
+
/* @__PURE__ */ jsx("path", { d: "M 21 2 L 8 2 L 2 8 L 8 14 L 21 14 Z" }),
|
|
6827
|
+
/* @__PURE__ */ jsx("line", { x1: "11", y1: "6", x2: "16", y2: "11" }),
|
|
6828
|
+
/* @__PURE__ */ jsx("line", { x1: "16", y1: "6", x2: "11", y2: "11" })
|
|
6829
|
+
] });
|
|
6830
|
+
}
|
|
6831
|
+
function WorldPositionPicker({ value, onChange, compact, setOnly, gotoOnly }) {
|
|
6832
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6833
|
+
!gotoOnly && /* @__PURE__ */ jsx(WorldPositionSetButton2, { value, onChange, compact }),
|
|
6834
|
+
!setOnly && /* @__PURE__ */ jsx(WorldPositionGotoButton2, { value, compact })
|
|
6835
|
+
] });
|
|
6836
|
+
}
|
|
6837
|
+
function WorldPositionSetButton2({
|
|
6838
|
+
value,
|
|
6839
|
+
onChange,
|
|
6840
|
+
compact
|
|
6841
|
+
}) {
|
|
6842
|
+
const theme = useMantineTheme();
|
|
6843
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
6844
|
+
const begin = useAdminToolStore((s) => s.begin);
|
|
6845
|
+
const onClick = async () => {
|
|
6846
|
+
const instructions = {
|
|
6847
|
+
title: locale("PickPositionTitle") || "Pick Position",
|
|
6848
|
+
hint: locale("PickPositionHint") || "Walk to where you want this set",
|
|
6849
|
+
keys: [
|
|
6850
|
+
{ key: "E", action: locale("Set") || "Set" },
|
|
6851
|
+
{ key: "\u232B", action: locale("Cancel") || "Cancel" }
|
|
6852
|
+
]
|
|
6853
|
+
};
|
|
6854
|
+
const pendingResult = begin({ id: "capturePosition", ...instructions });
|
|
6855
|
+
fetchNui("ADMIN_TOOL_BEGIN", { id: "capturePosition", instructions }).catch(() => {
|
|
6856
|
+
useAdminToolStore.getState().cancelActive();
|
|
6857
|
+
});
|
|
6858
|
+
const result = await pendingResult;
|
|
6859
|
+
if (result) onChange(result);
|
|
6860
|
+
};
|
|
6861
|
+
return /* @__PURE__ */ jsx(Tooltip, { label: locale("SetWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxs(
|
|
6862
|
+
motion.button,
|
|
6863
|
+
{
|
|
6864
|
+
onClick,
|
|
6865
|
+
whileHover: { background: alpha(color, 0.18) },
|
|
6866
|
+
whileTap: { scale: 0.95 },
|
|
6867
|
+
style: {
|
|
6868
|
+
background: alpha(color, 0.1),
|
|
6869
|
+
border: `0.1vh solid ${alpha(color, 0.35)}`,
|
|
6870
|
+
borderRadius: theme.radius.xs,
|
|
6871
|
+
padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
|
|
6872
|
+
cursor: "pointer",
|
|
6873
|
+
display: "flex",
|
|
6874
|
+
alignItems: "center",
|
|
6875
|
+
gap: compact ? "0.3vh" : "0.4vh"
|
|
6876
|
+
},
|
|
6877
|
+
children: [
|
|
6878
|
+
/* @__PURE__ */ jsx(Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
|
|
6879
|
+
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Set") })
|
|
6880
|
+
]
|
|
6881
|
+
}
|
|
6882
|
+
) });
|
|
6883
|
+
}
|
|
6884
|
+
function WorldPositionGotoButton2({
|
|
6885
|
+
value,
|
|
6886
|
+
compact
|
|
6887
|
+
}) {
|
|
6888
|
+
const theme = useMantineTheme();
|
|
6889
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
6890
|
+
const onClick = () => {
|
|
6891
|
+
fetchNui("ADMIN_TOOL_INVOKE", { id: "gotoCoord", value }).catch(() => {
|
|
6892
|
+
});
|
|
6893
|
+
};
|
|
6894
|
+
return /* @__PURE__ */ jsx(Tooltip, { label: locale("GotoWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxs(
|
|
6895
|
+
motion.button,
|
|
6896
|
+
{
|
|
6897
|
+
onClick,
|
|
6898
|
+
whileHover: { background: alpha(color, 0.18) },
|
|
6899
|
+
whileTap: { scale: 0.95 },
|
|
6900
|
+
style: {
|
|
6901
|
+
background: alpha(color, 0.1),
|
|
6902
|
+
border: `0.1vh solid ${alpha(color, 0.35)}`,
|
|
6903
|
+
borderRadius: theme.radius.xs,
|
|
6904
|
+
padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
|
|
6905
|
+
cursor: "pointer",
|
|
6906
|
+
display: "flex",
|
|
6907
|
+
alignItems: "center",
|
|
6908
|
+
gap: compact ? "0.3vh" : "0.4vh"
|
|
6909
|
+
},
|
|
6910
|
+
children: [
|
|
6911
|
+
/* @__PURE__ */ jsx(MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
|
|
6912
|
+
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Goto") })
|
|
6913
|
+
]
|
|
6914
|
+
}
|
|
6915
|
+
) });
|
|
6916
|
+
}
|
|
6917
|
+
function usePlayers(opts = {}) {
|
|
6918
|
+
const {
|
|
6919
|
+
includeOffline = false,
|
|
6920
|
+
search = "",
|
|
6921
|
+
limit = 50,
|
|
6922
|
+
staleTimeMs,
|
|
6923
|
+
refetchIntervalMs
|
|
6924
|
+
} = opts;
|
|
6925
|
+
const query = useQuery({
|
|
6926
|
+
queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
|
|
6927
|
+
queryFn: async () => {
|
|
6928
|
+
const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
|
|
6929
|
+
const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
|
|
6930
|
+
const result = await fetchNui(
|
|
6931
|
+
"ADMIN_TOOL_QUERY",
|
|
6932
|
+
payload,
|
|
6933
|
+
// Browser-dev fallback. Returns a couple of mock players so the
|
|
6934
|
+
// dev shell isn't blank.
|
|
6935
|
+
includeOffline ? [
|
|
6936
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
|
|
6937
|
+
{ id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
|
|
6938
|
+
] : [
|
|
6939
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
|
|
6940
|
+
]
|
|
6941
|
+
);
|
|
6942
|
+
return Array.isArray(result) ? result : [];
|
|
6943
|
+
},
|
|
6944
|
+
staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
|
|
6945
|
+
gcTime: 5 * 6e4,
|
|
6946
|
+
refetchInterval: refetchIntervalMs ?? false,
|
|
6947
|
+
refetchOnWindowFocus: false,
|
|
6948
|
+
placeholderData: includeOffline ? keepPreviousData : void 0
|
|
6949
|
+
});
|
|
6950
|
+
return {
|
|
6951
|
+
players: query.data ?? [],
|
|
6952
|
+
isLoading: query.isLoading,
|
|
6953
|
+
isFetching: query.isFetching,
|
|
6954
|
+
error: query.error ?? null,
|
|
6955
|
+
refresh: () => query.refetch()
|
|
6956
|
+
};
|
|
6957
|
+
}
|
|
6958
|
+
var DEBOUNCE_MS = 300;
|
|
6959
|
+
var ONLINE_COLOR = "#3FA83F";
|
|
6960
|
+
var OFFLINE_COLOR = "#E54141";
|
|
6961
|
+
function PlayerSelect({
|
|
6962
|
+
value,
|
|
6963
|
+
onChange,
|
|
6964
|
+
includeOffline = false,
|
|
6965
|
+
limit = 50,
|
|
6966
|
+
searchable: searchableProp,
|
|
6967
|
+
placeholder,
|
|
6968
|
+
nothingFoundMessage: nothingFoundMessageProp,
|
|
6969
|
+
loadingLabel = "Loading\u2026",
|
|
6970
|
+
onlineLabel = "Online",
|
|
6971
|
+
offlineLabel = "Offline",
|
|
6972
|
+
refreshLabel = "Refresh player list",
|
|
6973
|
+
...rest
|
|
6974
|
+
}) {
|
|
6975
|
+
const theme = useMantineTheme();
|
|
6976
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
6977
|
+
const [searchInput, setSearchInput] = useState("");
|
|
6978
|
+
const [debouncedSearch, setDebouncedSearch] = useState("");
|
|
6979
|
+
const { players, isFetching, refresh } = usePlayers({
|
|
6980
|
+
includeOffline,
|
|
6981
|
+
search: includeOffline ? debouncedSearch : void 0,
|
|
6982
|
+
limit
|
|
6983
|
+
});
|
|
6984
|
+
const sortedPlayers = useMemo(
|
|
6985
|
+
() => [...players].sort((a, b) => {
|
|
6986
|
+
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
6987
|
+
return a.charName.localeCompare(b.charName);
|
|
6988
|
+
}),
|
|
6989
|
+
[players]
|
|
6990
|
+
);
|
|
6991
|
+
const playerByCitizen = useMemo(() => {
|
|
6992
|
+
const m = /* @__PURE__ */ new Map();
|
|
6993
|
+
for (const p of sortedPlayers) m.set(p.citizenId, p);
|
|
6994
|
+
return m;
|
|
6995
|
+
}, [sortedPlayers]);
|
|
6996
|
+
const data = useMemo(() => {
|
|
6997
|
+
const items = sortedPlayers.map((p) => ({
|
|
6998
|
+
value: p.citizenId,
|
|
6999
|
+
label: formatLabel(p)
|
|
7000
|
+
}));
|
|
7001
|
+
if (value && !items.some((i) => i.value === value)) {
|
|
7002
|
+
items.unshift({ value, label: value });
|
|
7003
|
+
}
|
|
7004
|
+
return items;
|
|
7005
|
+
}, [sortedPlayers, value]);
|
|
7006
|
+
const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
|
|
7007
|
+
const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
|
|
7008
|
+
useEffect(() => {
|
|
7009
|
+
if (!includeOffline) return;
|
|
7010
|
+
if (selectedLabel && searchInput === selectedLabel) return;
|
|
7011
|
+
const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
|
|
7012
|
+
return () => clearTimeout(t3);
|
|
7013
|
+
}, [searchInput, includeOffline, selectedLabel]);
|
|
7014
|
+
const renderOption = ({ option }) => {
|
|
7015
|
+
const p = playerByCitizen.get(option.value);
|
|
7016
|
+
if (!p) return option.label;
|
|
7017
|
+
return /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
|
|
7018
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
|
|
7019
|
+
/* @__PURE__ */ jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
|
|
7020
|
+
] });
|
|
7021
|
+
};
|
|
7022
|
+
return /* @__PURE__ */ jsx(
|
|
7023
|
+
Select,
|
|
7024
|
+
{
|
|
7025
|
+
...rest,
|
|
7026
|
+
data,
|
|
7027
|
+
value: value ?? null,
|
|
7028
|
+
onChange: (v) => {
|
|
7029
|
+
if (!v) return onChange(null);
|
|
7030
|
+
const player = playerByCitizen.get(v) ?? null;
|
|
7031
|
+
onChange(player);
|
|
7032
|
+
},
|
|
7033
|
+
searchable: searchableProp ?? true,
|
|
7034
|
+
searchValue: includeOffline ? searchInput : void 0,
|
|
7035
|
+
onSearchChange: includeOffline ? setSearchInput : void 0,
|
|
7036
|
+
placeholder,
|
|
7037
|
+
nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
|
|
7038
|
+
maxDropdownHeight: 300,
|
|
7039
|
+
renderOption,
|
|
7040
|
+
leftSection: selectedPlayer ? /* @__PURE__ */ jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
|
|
7041
|
+
rightSectionWidth: "3.5vh",
|
|
7042
|
+
rightSectionPointerEvents: "all",
|
|
7043
|
+
rightSection: /* @__PURE__ */ jsx(
|
|
7044
|
+
ActionIcon,
|
|
7045
|
+
{
|
|
7046
|
+
variant: "subtle",
|
|
7047
|
+
size: "sm",
|
|
7048
|
+
onClick: (e) => {
|
|
7049
|
+
e.stopPropagation();
|
|
7050
|
+
refresh();
|
|
7051
|
+
},
|
|
7052
|
+
"aria-label": refreshLabel,
|
|
7053
|
+
title: refreshLabel,
|
|
7054
|
+
style: { marginRight: "0.6vh" },
|
|
7055
|
+
children: isFetching ? /* @__PURE__ */ jsx(Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsx(RefreshCw, { size: "1.4vh", color })
|
|
7056
|
+
}
|
|
7057
|
+
)
|
|
7058
|
+
}
|
|
7059
|
+
);
|
|
7060
|
+
}
|
|
7061
|
+
function StatusDot({ online, onlineLabel, offlineLabel }) {
|
|
7062
|
+
return /* @__PURE__ */ jsx(
|
|
7063
|
+
"span",
|
|
7064
|
+
{
|
|
7065
|
+
title: online ? onlineLabel : offlineLabel,
|
|
7066
|
+
style: {
|
|
7067
|
+
display: "inline-block",
|
|
7068
|
+
width: "0.9vh",
|
|
7069
|
+
height: "0.9vh",
|
|
7070
|
+
borderRadius: "50%",
|
|
7071
|
+
backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
|
|
7072
|
+
boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
|
|
7073
|
+
flexShrink: 0
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
);
|
|
7077
|
+
}
|
|
7078
|
+
function formatLabel(p) {
|
|
7079
|
+
const parts = [p.charName || p.citizenId];
|
|
7080
|
+
if (p.online) {
|
|
7081
|
+
if (p.name) parts.push(p.name);
|
|
7082
|
+
if (p.id != null) parts.push(`#${p.id}`);
|
|
7083
|
+
}
|
|
7084
|
+
return parts.join(" \xB7 ");
|
|
7085
|
+
}
|
|
7086
|
+
function usePickDoor() {
|
|
7087
|
+
const begin = useAdminToolStore((s) => s.begin);
|
|
7088
|
+
return async () => {
|
|
7089
|
+
const pending = begin({
|
|
7090
|
+
id: "pickDoor",
|
|
7091
|
+
title: locale("PickDoorTitle"),
|
|
7092
|
+
hint: locale("PickDoorHint"),
|
|
7093
|
+
keys: [
|
|
7094
|
+
{ key: "LMB", action: locale("Toggle") },
|
|
7095
|
+
{ key: "E", action: locale("Confirm") },
|
|
7096
|
+
{ key: "BACKSPACE", action: locale("Cancel") }
|
|
7097
|
+
]
|
|
7098
|
+
});
|
|
7099
|
+
fetchNui("ADMIN_TOOL_BEGIN", { id: "pickDoor" }).catch(() => {
|
|
7100
|
+
useAdminToolStore.getState().cancelActive();
|
|
7101
|
+
});
|
|
7102
|
+
return await pending;
|
|
7103
|
+
};
|
|
7104
|
+
}
|
|
7105
|
+
function DoorPickerButton({
|
|
7106
|
+
onPick,
|
|
7107
|
+
compact,
|
|
7108
|
+
label,
|
|
7109
|
+
tooltip
|
|
7110
|
+
}) {
|
|
7111
|
+
const theme = useMantineTheme();
|
|
7112
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
7113
|
+
const pickDoor = usePickDoor();
|
|
7114
|
+
const onClick = async () => {
|
|
7115
|
+
const picked = await pickDoor();
|
|
7116
|
+
if (picked && picked.doors.length > 0) onPick(picked);
|
|
7117
|
+
};
|
|
7118
|
+
return /* @__PURE__ */ jsx(
|
|
7119
|
+
Tooltip,
|
|
7120
|
+
{
|
|
7121
|
+
label: tooltip ?? locale("PickDoorTooltip"),
|
|
7122
|
+
position: "top",
|
|
7123
|
+
withArrow: true,
|
|
7124
|
+
withinPortal: true,
|
|
7125
|
+
zIndex: 2e3,
|
|
7126
|
+
children: /* @__PURE__ */ jsxs(
|
|
7127
|
+
motion.button,
|
|
7128
|
+
{
|
|
7129
|
+
onClick,
|
|
7130
|
+
whileHover: { background: alpha(color, 0.18) },
|
|
7131
|
+
whileTap: { scale: 0.95 },
|
|
7132
|
+
style: {
|
|
7133
|
+
background: alpha(color, 0.1),
|
|
7134
|
+
border: `0.1vh solid ${alpha(color, 0.35)}`,
|
|
7135
|
+
borderRadius: theme.radius.xs,
|
|
7136
|
+
padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
|
|
7137
|
+
cursor: "pointer",
|
|
7138
|
+
display: "flex",
|
|
7139
|
+
alignItems: "center",
|
|
7140
|
+
gap: compact ? "0.3vh" : "0.4vh"
|
|
7141
|
+
},
|
|
7142
|
+
children: [
|
|
7143
|
+
/* @__PURE__ */ jsx(DoorOpen, { size: compact ? "1.1vh" : "1.3vh", color }),
|
|
7144
|
+
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label ?? locale("PickDoor") })
|
|
7145
|
+
]
|
|
7146
|
+
}
|
|
7147
|
+
)
|
|
7148
|
+
}
|
|
7149
|
+
);
|
|
7150
|
+
}
|
|
7151
|
+
var MapImpl = memo(({
|
|
7152
|
+
children,
|
|
7153
|
+
initialZoom = 2,
|
|
7154
|
+
initialCenter = gameToMap(0, 0),
|
|
7155
|
+
mapStyle = "game"
|
|
7156
|
+
}) => {
|
|
7157
|
+
const mapRef = useRef(null);
|
|
7158
|
+
return /* @__PURE__ */ jsxs(
|
|
7159
|
+
MapContainer,
|
|
7160
|
+
{
|
|
7161
|
+
maxBoundsViscosity: 1,
|
|
7162
|
+
preferCanvas: true,
|
|
7163
|
+
zoom: Math.round(initialZoom),
|
|
7164
|
+
zoomSnap: 1,
|
|
7165
|
+
zoomDelta: 1,
|
|
7166
|
+
zoomControl: false,
|
|
7167
|
+
crs: CRS.Simple,
|
|
7168
|
+
style: {
|
|
7169
|
+
height: "100%",
|
|
7170
|
+
width: "100%",
|
|
7171
|
+
overflow: "hidden",
|
|
7172
|
+
outline: "none !important",
|
|
7173
|
+
border: "none !important",
|
|
7174
|
+
boxShadow: "none !important",
|
|
7175
|
+
backgroundColor: "#384950"
|
|
7176
|
+
},
|
|
7177
|
+
center: initialCenter,
|
|
7178
|
+
attributionControl: false,
|
|
7179
|
+
doubleClickZoom: false,
|
|
7180
|
+
inertia: false,
|
|
7181
|
+
zoomAnimation: false,
|
|
7182
|
+
ref: mapRef,
|
|
7183
|
+
maxBounds: [[-250, -250], [250, 250]],
|
|
7184
|
+
children: [
|
|
7185
|
+
/* @__PURE__ */ jsx(MapLayer, { mapLayer: mapStyle }),
|
|
7186
|
+
children
|
|
7187
|
+
]
|
|
7188
|
+
}
|
|
7189
|
+
);
|
|
7190
|
+
});
|
|
7191
|
+
MapImpl.displayName = "DirkMap";
|
|
7192
|
+
var Map2 = MapImpl;
|
|
7193
|
+
var MapLayer = ({ mapLayer }) => {
|
|
7194
|
+
const map = useMap();
|
|
7195
|
+
const layerRef = useRef(null);
|
|
7196
|
+
useEffect(() => {
|
|
7197
|
+
if (layerRef.current) {
|
|
7198
|
+
map.removeLayer(layerRef.current);
|
|
7199
|
+
}
|
|
7200
|
+
const layer = tileLayer(
|
|
7201
|
+
`https://s.rsg.sc/sc/images/games/GTAV/map/${mapLayer}/{z}/{x}/{y}.jpg`,
|
|
7202
|
+
{
|
|
7203
|
+
maxZoom: 6,
|
|
7204
|
+
minZoom: 4,
|
|
7205
|
+
bounds: latLngBounds(latLng(0, 128), latLng(-192, 0)),
|
|
7206
|
+
tileSize: 256,
|
|
7207
|
+
updateWhenZooming: false,
|
|
7208
|
+
keepBuffer: 2,
|
|
7209
|
+
opacity: 0.75
|
|
7210
|
+
}
|
|
7211
|
+
);
|
|
7212
|
+
layer.addTo(map);
|
|
7213
|
+
layerRef.current = layer;
|
|
7214
|
+
return () => {
|
|
7215
|
+
if (layerRef.current) {
|
|
7216
|
+
map.removeLayer(layerRef.current);
|
|
7217
|
+
}
|
|
7218
|
+
};
|
|
7219
|
+
}, [mapLayer, map]);
|
|
7220
|
+
return null;
|
|
7221
|
+
};
|
|
7222
|
+
function ZoomControls() {
|
|
7223
|
+
const theme = useMantineTheme();
|
|
7224
|
+
const map = useMap();
|
|
7225
|
+
const buttons = [
|
|
7226
|
+
{ Icon: Plus, fn: () => map.zoomIn() },
|
|
7227
|
+
{ Icon: Minus, fn: () => map.zoomOut() }
|
|
7228
|
+
];
|
|
7229
|
+
return /* @__PURE__ */ jsx(
|
|
7230
|
+
motion.div,
|
|
7231
|
+
{
|
|
7232
|
+
style: {
|
|
7233
|
+
position: "absolute",
|
|
7234
|
+
right: "2vh",
|
|
7235
|
+
top: "2vh",
|
|
7236
|
+
display: "flex",
|
|
7237
|
+
flexDirection: "column",
|
|
7238
|
+
zIndex: 999999,
|
|
7239
|
+
boxShadow: `0 0 1vh ${alpha(theme.colors.dark[9], 0.85)}`,
|
|
7240
|
+
background: alpha(theme.colors.dark[9], 0.85),
|
|
7241
|
+
borderRadius: theme.radius.xs
|
|
7242
|
+
},
|
|
7243
|
+
initial: { opacity: 0, y: -20 },
|
|
7244
|
+
animate: { opacity: 1, y: 0 },
|
|
7245
|
+
exit: { opacity: 0, y: -20 },
|
|
7246
|
+
children: buttons.map(({ Icon, fn }, i) => /* @__PURE__ */ jsx(
|
|
7247
|
+
motion.div,
|
|
7248
|
+
{
|
|
7249
|
+
whileHover: { scale: 1.1, filter: "brightness(1.5)" },
|
|
7250
|
+
onClick: fn,
|
|
7251
|
+
style: {
|
|
7252
|
+
padding: theme.spacing.xs,
|
|
7253
|
+
cursor: "pointer",
|
|
7254
|
+
display: "flex"
|
|
7255
|
+
},
|
|
7256
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 34, color: theme.colors.gray[5] })
|
|
7257
|
+
},
|
|
7258
|
+
i
|
|
7259
|
+
))
|
|
7260
|
+
}
|
|
7261
|
+
);
|
|
7262
|
+
}
|
|
7263
|
+
var DEFAULT_SPRITE = 162;
|
|
7264
|
+
var DEFAULT_COLOR = 5;
|
|
7265
|
+
function BlipMarker({
|
|
7266
|
+
position,
|
|
7267
|
+
sprite,
|
|
7268
|
+
color,
|
|
7269
|
+
scale = 1,
|
|
7270
|
+
onClick,
|
|
7271
|
+
selected,
|
|
7272
|
+
disabled,
|
|
7273
|
+
fallbackSprite = DEFAULT_SPRITE,
|
|
7274
|
+
fallbackColor = DEFAULT_COLOR
|
|
7275
|
+
}) {
|
|
7276
|
+
const [hovered, setHovered] = useState(false);
|
|
7277
|
+
const mapCoords = useMemo(() => gameToMap(position.x, position.y), [position.x, position.y]);
|
|
7278
|
+
const effectiveSprite = sprite ?? fallbackSprite;
|
|
7279
|
+
const effectiveColor = color ?? fallbackColor;
|
|
7280
|
+
const url = blipUrlForSprite(effectiveSprite);
|
|
7281
|
+
const colorHex = getBlipColor(effectiveColor)?.hex ?? "#ffffff";
|
|
7282
|
+
const handleClick = (e) => {
|
|
7283
|
+
e.originalEvent?.stopPropagation?.();
|
|
7284
|
+
if (disabled) return;
|
|
7285
|
+
onClick?.();
|
|
7286
|
+
};
|
|
7287
|
+
const baseSize = 1.8 * scale;
|
|
7288
|
+
const size = `${baseSize}vh`;
|
|
7289
|
+
const ringSize = `${baseSize * 1.6}vh`;
|
|
7290
|
+
return /* @__PURE__ */ jsx(
|
|
7291
|
+
Marker,
|
|
7292
|
+
{
|
|
7293
|
+
position: mapCoords,
|
|
7294
|
+
eventHandlers: onClick ? { click: handleClick } : void 0,
|
|
7295
|
+
icon: /* @__PURE__ */ jsxs(
|
|
7296
|
+
motion.div,
|
|
7297
|
+
{
|
|
7298
|
+
onHoverStart: () => setHovered(true),
|
|
7299
|
+
onHoverEnd: () => setHovered(false),
|
|
7300
|
+
style: {
|
|
7301
|
+
position: "relative",
|
|
7302
|
+
display: "flex",
|
|
7303
|
+
alignItems: "center",
|
|
7304
|
+
justifyContent: "center",
|
|
7305
|
+
cursor: disabled ? "not-allowed" : onClick ? "pointer" : "default",
|
|
7306
|
+
opacity: disabled ? 0.35 : 1,
|
|
7307
|
+
width: size,
|
|
7308
|
+
height: size
|
|
7309
|
+
},
|
|
7310
|
+
animate: { scale: hovered && !disabled ? 1.2 : selected ? 1.15 : 1 },
|
|
7311
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
7312
|
+
children: [
|
|
7313
|
+
(selected || hovered) && !disabled && /* @__PURE__ */ jsx(
|
|
7314
|
+
motion.div,
|
|
7315
|
+
{
|
|
7316
|
+
style: {
|
|
7317
|
+
position: "absolute",
|
|
7318
|
+
width: ringSize,
|
|
7319
|
+
height: ringSize,
|
|
7320
|
+
borderRadius: "50%",
|
|
7321
|
+
border: `0.18vh solid ${alpha(colorHex, 0.85)}`,
|
|
7322
|
+
boxShadow: `0 0 1vh ${alpha(colorHex, 0.55)}`
|
|
7323
|
+
},
|
|
7324
|
+
initial: { opacity: 0, scale: 0.8 },
|
|
7325
|
+
animate: { opacity: 1, scale: 1 }
|
|
7326
|
+
}
|
|
7327
|
+
),
|
|
7328
|
+
url && /* @__PURE__ */ jsx(
|
|
7329
|
+
"div",
|
|
7330
|
+
{
|
|
7331
|
+
style: {
|
|
7332
|
+
width: size,
|
|
7333
|
+
height: size,
|
|
7334
|
+
backgroundColor: colorHex,
|
|
7335
|
+
WebkitMaskImage: `url(${url})`,
|
|
7336
|
+
maskImage: `url(${url})`,
|
|
7337
|
+
WebkitMaskRepeat: "no-repeat",
|
|
7338
|
+
maskRepeat: "no-repeat",
|
|
7339
|
+
WebkitMaskPosition: "center",
|
|
7340
|
+
maskPosition: "center",
|
|
7341
|
+
WebkitMaskSize: "contain",
|
|
7342
|
+
maskSize: "contain",
|
|
7343
|
+
filter: [
|
|
7344
|
+
"drop-shadow(0.12vh 0 0 #000)",
|
|
7345
|
+
"drop-shadow(-0.12vh 0 0 #000)",
|
|
7346
|
+
"drop-shadow(0 0.12vh 0 #000)",
|
|
7347
|
+
"drop-shadow(0 -0.12vh 0 #000)",
|
|
7348
|
+
`drop-shadow(0 0 0.4vh ${alpha("#000", 0.7)})`
|
|
7349
|
+
].join(" "),
|
|
7350
|
+
pointerEvents: "none",
|
|
7351
|
+
userSelect: "none"
|
|
7352
|
+
}
|
|
7353
|
+
}
|
|
7354
|
+
)
|
|
7355
|
+
]
|
|
7356
|
+
}
|
|
7357
|
+
)
|
|
7358
|
+
}
|
|
7359
|
+
);
|
|
7360
|
+
}
|
|
6535
7361
|
|
|
6536
|
-
export { AccountSelect, AdminPageTitle, AnimPostFxSelect, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, DiscordRoleSelect, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, LevelBanner, LevelPanel, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PositionPicker, PromptModal, ScenarioSelect, SegmentedControl, SegmentedProgress, SelectItem, SwitchPanel, TestBed, ThemeOverrideSection, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionSetButton, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore };
|
|
7362
|
+
export { AccountSelect, AdminPageTitle, AnimPostFxSelect, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BlipMarker, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, DiscordRoleSelect, DoorPickerButton, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, InstructionPanel, LevelBanner, LevelPanel, Map2 as Map, MapLayer, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PlayerSelect, PositionPicker, PromptModal, ScenarioSelect, SegmentedControl, SegmentedProgress, SelectItem, SwitchPanel, TestBed, ThemeOverrideSection, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionPicker, WorldPositionSetButton, ZoomControls, blipUrl, blipUrlForSprite, getBlipColor, getBlipEntry, useAdminToolStore, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore, usePickDoor };
|
|
6537
7363
|
//# sourceMappingURL=index.js.map
|
|
6538
7364
|
//# sourceMappingURL=index.js.map
|