dirk-cfx-react 1.1.86 → 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 +873 -33
- 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 +864 -38
- 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 +1440 -452
- 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 +1343 -375
- 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
|
@@ -11,7 +11,22 @@ var clickSoundUrl = require('../click_sound-PNCRRTM4.mp3');
|
|
|
11
11
|
var hoverSoundUrl = require('../hover_sound-NBUA222C.mp3');
|
|
12
12
|
var notifications = require('@mantine/notifications');
|
|
13
13
|
var reactQuery = require('@tanstack/react-query');
|
|
14
|
+
require('@mantine/core/styles.css');
|
|
15
|
+
require('@mantine/notifications/styles.css');
|
|
16
|
+
require('./styles/fonts.css');
|
|
17
|
+
require('./styles/notify.css');
|
|
18
|
+
require('./styles/scrollBar.css');
|
|
19
|
+
require('./styles/tornEdge.css');
|
|
20
|
+
var fontawesomeSvgCore = require('@fortawesome/fontawesome-svg-core');
|
|
21
|
+
var freeBrandsSvgIcons = require('@fortawesome/free-brands-svg-icons');
|
|
22
|
+
var freeRegularSvgIcons = require('@fortawesome/free-regular-svg-icons');
|
|
23
|
+
var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
|
|
14
24
|
var colorsGenerator = require('@mantine/colors-generator');
|
|
25
|
+
var reactDom = require('react-dom');
|
|
26
|
+
var leaflet = require('leaflet');
|
|
27
|
+
require('leaflet/dist/leaflet.css');
|
|
28
|
+
var reactLeaflet = require('react-leaflet');
|
|
29
|
+
var reactLeafletComponentMarker = require('@adamscybot/react-leaflet-component-marker');
|
|
15
30
|
|
|
16
31
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
32
|
|
|
@@ -960,6 +975,18 @@ var BLIP_COLOR_DATA = BLIP_COLORS.map(([id, label]) => ({
|
|
|
960
975
|
}));
|
|
961
976
|
var blipEntryMap = new Map(BLIP_ENTRIES.map(([id, name, ext]) => [String(id), { id, name, ext }]));
|
|
962
977
|
var blipColorMap = new Map(BLIP_COLORS.map(([id, label, hex]) => [String(id), { id, label, hex }]));
|
|
978
|
+
function getBlipEntry(spriteId) {
|
|
979
|
+
if (spriteId == null) return void 0;
|
|
980
|
+
return blipEntryMap.get(String(spriteId));
|
|
981
|
+
}
|
|
982
|
+
function getBlipColor(colorId) {
|
|
983
|
+
if (colorId == null) return void 0;
|
|
984
|
+
return blipColorMap.get(String(colorId));
|
|
985
|
+
}
|
|
986
|
+
function blipUrlForSprite(spriteId) {
|
|
987
|
+
const entry = getBlipEntry(spriteId);
|
|
988
|
+
return entry ? blipUrl(entry.name, entry.ext) : null;
|
|
989
|
+
}
|
|
963
990
|
var renderBlipOption = ({ option }) => {
|
|
964
991
|
const entry = blipEntryMap.get(option.value);
|
|
965
992
|
if (!entry) return option.label;
|
|
@@ -1237,11 +1264,11 @@ var colorNames = {
|
|
|
1237
1264
|
Yellow: { r: 255, g: 255, b: 0 },
|
|
1238
1265
|
YellowGreen: { r: 154, g: 205, b: 50 }
|
|
1239
1266
|
};
|
|
1240
|
-
function colorWithAlpha(color,
|
|
1267
|
+
function colorWithAlpha(color, alpha17) {
|
|
1241
1268
|
const lowerCasedColor = color.toLowerCase();
|
|
1242
1269
|
if (colorNames[lowerCasedColor]) {
|
|
1243
1270
|
const rgb = colorNames[lowerCasedColor];
|
|
1244
|
-
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
|
|
1271
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha17})`;
|
|
1245
1272
|
}
|
|
1246
1273
|
if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
|
|
1247
1274
|
const hex = color.slice(1);
|
|
@@ -1249,12 +1276,12 @@ function colorWithAlpha(color, alpha12) {
|
|
|
1249
1276
|
const r = bigint >> 16 & 255;
|
|
1250
1277
|
const g = bigint >> 8 & 255;
|
|
1251
1278
|
const b = bigint & 255;
|
|
1252
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
1279
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha17})`;
|
|
1253
1280
|
}
|
|
1254
1281
|
if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
|
|
1255
1282
|
const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
|
|
1256
1283
|
if (result) {
|
|
1257
|
-
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${
|
|
1284
|
+
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha17})`;
|
|
1258
1285
|
}
|
|
1259
1286
|
}
|
|
1260
1287
|
return color;
|
|
@@ -1349,9 +1376,22 @@ if (typeof window !== "undefined") {
|
|
|
1349
1376
|
const msg = event.data;
|
|
1350
1377
|
if (!msg || msg.action !== "UPDATE_DIRK_LIB_LOCALES") return;
|
|
1351
1378
|
if (!msg.data || typeof msg.data !== "object") return;
|
|
1379
|
+
if (Object.keys(msg.data).length === 0) return;
|
|
1352
1380
|
localeStore.setState({ locales: msg.data });
|
|
1353
1381
|
});
|
|
1354
1382
|
}
|
|
1383
|
+
|
|
1384
|
+
// src/utils/map.ts
|
|
1385
|
+
var mapCenter = [-119.43, 58.84];
|
|
1386
|
+
var latPr100 = 1.421;
|
|
1387
|
+
function gameToMap(x, y) {
|
|
1388
|
+
return [
|
|
1389
|
+
mapCenter[0] + latPr100 / 100 * y,
|
|
1390
|
+
// lng
|
|
1391
|
+
mapCenter[1] + latPr100 / 100 * x
|
|
1392
|
+
// lat
|
|
1393
|
+
];
|
|
1394
|
+
}
|
|
1355
1395
|
var useItems = zustand.create(() => ({}));
|
|
1356
1396
|
var useItemsList = (excludeItemNames = []) => {
|
|
1357
1397
|
const excludeSet = new Set(excludeItemNames);
|
|
@@ -1371,15 +1411,21 @@ var useFrameworkGroups = zustand.create(() => ({
|
|
|
1371
1411
|
gangs: [],
|
|
1372
1412
|
loaded: false
|
|
1373
1413
|
}));
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1414
|
+
var frameworkGroupsRequested = false;
|
|
1415
|
+
function ensureFrameworkGroups() {
|
|
1416
|
+
if (frameworkGroupsRequested) return;
|
|
1417
|
+
frameworkGroupsRequested = true;
|
|
1418
|
+
fetchNui("GET_FRAMEWORK_GROUPS", void 0).then((data) => {
|
|
1419
|
+
useFrameworkGroups.setState({
|
|
1420
|
+
jobs: Array.isArray(data?.jobs) ? data.jobs : [],
|
|
1421
|
+
gangs: Array.isArray(data?.gangs) ? data.gangs : [],
|
|
1422
|
+
loaded: true
|
|
1423
|
+
});
|
|
1424
|
+
}).catch(() => {
|
|
1425
|
+
frameworkGroupsRequested = false;
|
|
1426
|
+
useFrameworkGroups.setState({ loaded: true });
|
|
1379
1427
|
});
|
|
1380
|
-
}
|
|
1381
|
-
useFrameworkGroups.setState({ loaded: true });
|
|
1382
|
-
});
|
|
1428
|
+
}
|
|
1383
1429
|
|
|
1384
1430
|
// src/utils/inputMapper.ts
|
|
1385
1431
|
var INPUT_MAPPER_PRIMARY_OPTIONS = [
|
|
@@ -2388,7 +2434,7 @@ function NavigationProvider({ children, defaultPage }) {
|
|
|
2388
2434
|
}
|
|
2389
2435
|
function NavBar(props) {
|
|
2390
2436
|
const pageId = useNavigation((state) => state.pageId);
|
|
2391
|
-
const
|
|
2437
|
+
const store2 = useNavigationStore();
|
|
2392
2438
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2393
2439
|
SegmentedControl,
|
|
2394
2440
|
{
|
|
@@ -2397,7 +2443,7 @@ function NavBar(props) {
|
|
|
2397
2443
|
value: pageId,
|
|
2398
2444
|
items: props.items,
|
|
2399
2445
|
onChange: (value) => {
|
|
2400
|
-
|
|
2446
|
+
store2.setState({ pageId: value });
|
|
2401
2447
|
}
|
|
2402
2448
|
}
|
|
2403
2449
|
);
|
|
@@ -3704,34 +3750,82 @@ function FormProvider({
|
|
|
3704
3750
|
return /* @__PURE__ */ jsxRuntime.jsx(FormContext.Provider, { value: storeRef.current, children });
|
|
3705
3751
|
}
|
|
3706
3752
|
function useForm() {
|
|
3707
|
-
const
|
|
3708
|
-
if (!
|
|
3753
|
+
const store2 = react.useContext(FormContext);
|
|
3754
|
+
if (!store2) {
|
|
3709
3755
|
throw new Error("useForm must be used inside <FormProvider>");
|
|
3710
3756
|
}
|
|
3711
|
-
const state = zustand.useStore(
|
|
3757
|
+
const state = zustand.useStore(store2);
|
|
3712
3758
|
const changedFields = react.useMemo(() => {
|
|
3713
3759
|
return collectChangedPaths(state.values, state.initialValues);
|
|
3714
3760
|
}, [state.values, state.initialValues]);
|
|
3715
3761
|
return { ...state, changedFields, changedCount: changedFields.length };
|
|
3716
3762
|
}
|
|
3717
3763
|
function useFormField(path) {
|
|
3718
|
-
const
|
|
3719
|
-
if (!
|
|
3764
|
+
const store2 = react.useContext(FormContext);
|
|
3765
|
+
if (!store2) {
|
|
3720
3766
|
throw new Error("useFormField must be used inside <FormProvider>");
|
|
3721
3767
|
}
|
|
3722
|
-
return zustand.useStore(
|
|
3768
|
+
return zustand.useStore(store2, (s) => getNested(s.values, path));
|
|
3723
3769
|
}
|
|
3724
3770
|
function useFormActions() {
|
|
3725
|
-
const
|
|
3726
|
-
if (!
|
|
3771
|
+
const store2 = react.useContext(FormContext);
|
|
3772
|
+
if (!store2) {
|
|
3727
3773
|
throw new Error("useFormActions must be used inside <FormProvider>");
|
|
3728
3774
|
}
|
|
3729
|
-
return
|
|
3775
|
+
return store2.getState();
|
|
3776
|
+
}
|
|
3777
|
+
var store = /* @__PURE__ */ new Map();
|
|
3778
|
+
var listeners = /* @__PURE__ */ new Map();
|
|
3779
|
+
function notify(key) {
|
|
3780
|
+
const ls = listeners.get(key);
|
|
3781
|
+
if (!ls) return;
|
|
3782
|
+
ls.forEach((fn) => fn());
|
|
3783
|
+
}
|
|
3784
|
+
function useAdminState(key, initial) {
|
|
3785
|
+
const [, setTick] = react.useState(0);
|
|
3786
|
+
react.useEffect(() => {
|
|
3787
|
+
const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
3788
|
+
listeners.set(key, set);
|
|
3789
|
+
const handler = () => setTick((t3) => t3 + 1);
|
|
3790
|
+
set.add(handler);
|
|
3791
|
+
return () => {
|
|
3792
|
+
set.delete(handler);
|
|
3793
|
+
if (set.size === 0) listeners.delete(key);
|
|
3794
|
+
};
|
|
3795
|
+
}, [key]);
|
|
3796
|
+
const value = store.has(key) ? store.get(key) : initial;
|
|
3797
|
+
const setValue = (v) => {
|
|
3798
|
+
const next = typeof v === "function" ? v(value) : v;
|
|
3799
|
+
store.set(key, next);
|
|
3800
|
+
notify(key);
|
|
3801
|
+
};
|
|
3802
|
+
return [value, setValue];
|
|
3730
3803
|
}
|
|
3731
3804
|
function getScriptConfigInstance() {
|
|
3732
3805
|
throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
|
|
3733
3806
|
}
|
|
3734
|
-
var
|
|
3807
|
+
var useAdminToolStore = zustand.create((set, get) => ({
|
|
3808
|
+
active: null,
|
|
3809
|
+
begin: (spec) => new Promise((resolve) => {
|
|
3810
|
+
const prev = get().active;
|
|
3811
|
+
if (prev) prev.resolve(null);
|
|
3812
|
+
set({ active: { ...spec, resolve } });
|
|
3813
|
+
}),
|
|
3814
|
+
resolveActive: (value) => {
|
|
3815
|
+
const cur = get().active;
|
|
3816
|
+
if (!cur) return;
|
|
3817
|
+
cur.resolve(value);
|
|
3818
|
+
set({ active: null });
|
|
3819
|
+
},
|
|
3820
|
+
cancelActive: () => {
|
|
3821
|
+
const cur = get().active;
|
|
3822
|
+
if (!cur) return;
|
|
3823
|
+
cur.resolve(null);
|
|
3824
|
+
set({ active: null });
|
|
3825
|
+
}
|
|
3826
|
+
}));
|
|
3827
|
+
fontawesomeSvgCore.library.add(freeSolidSvgIcons.fas, freeRegularSvgIcons.far, freeBrandsSvgIcons.fab);
|
|
3828
|
+
var dirkQueryClient = new reactQuery.QueryClient({
|
|
3735
3829
|
defaultOptions: { queries: { staleTime: 3e4, gcTime: 5 * 6e4 } }
|
|
3736
3830
|
});
|
|
3737
3831
|
function NavItemButton({
|
|
@@ -4062,7 +4156,7 @@ function ConfigPanelInner({
|
|
|
4062
4156
|
const theme = core.useMantineTheme();
|
|
4063
4157
|
const color = theme.colors[theme.primaryColor][5];
|
|
4064
4158
|
const version = useSettings((s) => s.resourceVersion);
|
|
4065
|
-
const [activeTab, setActiveTab] =
|
|
4159
|
+
const [activeTab, setActiveTab] = useAdminState("__dirkConfigPanel:activeTab", navItems[0]?.id ?? "");
|
|
4066
4160
|
const firstMountRef = react.useRef(true);
|
|
4067
4161
|
const [jsonOpen, setJsonOpen] = react.useState(false);
|
|
4068
4162
|
const [historyOpen, setHistoryOpen] = react.useState(false);
|
|
@@ -4105,8 +4199,8 @@ function ConfigPanelInner({
|
|
|
4105
4199
|
setResetOpen(false);
|
|
4106
4200
|
const result = await resetConfig();
|
|
4107
4201
|
if (result?.success) {
|
|
4108
|
-
const { store } = getScriptConfigInstance();
|
|
4109
|
-
form.reinitialize(cloneConfig(
|
|
4202
|
+
const { store: store2 } = getScriptConfigInstance();
|
|
4203
|
+
form.reinitialize(cloneConfig(store2.getState()));
|
|
4110
4204
|
notifications.notifications.show({
|
|
4111
4205
|
color: "green",
|
|
4112
4206
|
title: locale("ConfigResetSuccessTitle"),
|
|
@@ -4334,13 +4428,13 @@ function ServerOnlyFetcher() {
|
|
|
4334
4428
|
var defaultOnClose = () => fetchNui("CLOSE_ADMIN_SECTION");
|
|
4335
4429
|
function ConfigPanel(props) {
|
|
4336
4430
|
const { open, onClose = defaultOnClose } = props;
|
|
4337
|
-
const { store, updateConfig } = getScriptConfigInstance();
|
|
4431
|
+
const { store: store2, updateConfig } = getScriptConfigInstance();
|
|
4338
4432
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
4339
4433
|
if (!open) return null;
|
|
4340
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4434
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4341
4435
|
FormProvider,
|
|
4342
4436
|
{
|
|
4343
|
-
initialValues: cloneConfig(
|
|
4437
|
+
initialValues: cloneConfig(store2.getState()),
|
|
4344
4438
|
onSubmit: async (form) => {
|
|
4345
4439
|
if (isSaving) return;
|
|
4346
4440
|
setIsSaving(true);
|
|
@@ -4348,7 +4442,7 @@ function ConfigPanel(props) {
|
|
|
4348
4442
|
const result = await updateConfig(form.values);
|
|
4349
4443
|
if (result?.success) {
|
|
4350
4444
|
form.reinitialize(cloneConfig(form.values));
|
|
4351
|
-
|
|
4445
|
+
dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
|
|
4352
4446
|
useMissingItemsAudit.getState().refresh();
|
|
4353
4447
|
notifications.notifications.show({
|
|
4354
4448
|
color: "green",
|
|
@@ -4358,7 +4452,7 @@ function ConfigPanel(props) {
|
|
|
4358
4452
|
});
|
|
4359
4453
|
return;
|
|
4360
4454
|
}
|
|
4361
|
-
form.reinitialize(cloneConfig(
|
|
4455
|
+
form.reinitialize(cloneConfig(store2.getState()));
|
|
4362
4456
|
const err = result?._error || "Unknown";
|
|
4363
4457
|
console.warn(`[ConfigPanel] config save failed: ${err}`);
|
|
4364
4458
|
const titleKey = err === "NoPermission" ? "ConfigSaveNoPermissionTitle" : err === "VersionConflict" ? "ConfigSaveVersionConflictTitle" : err === "NotReady" ? "ConfigSaveNotReadyTitle" : "ConfigSaveFailedTitle";
|
|
@@ -4855,6 +4949,9 @@ function GroupName(props) {
|
|
|
4855
4949
|
const ctx = react.useContext(GroupSelectContext);
|
|
4856
4950
|
const jobs = useFrameworkGroups((s) => s.jobs);
|
|
4857
4951
|
const gangs = useFrameworkGroups((s) => s.gangs);
|
|
4952
|
+
react.useEffect(() => {
|
|
4953
|
+
ensureFrameworkGroups();
|
|
4954
|
+
}, []);
|
|
4858
4955
|
const inCompound = ctx !== null;
|
|
4859
4956
|
const currentValue = inCompound ? ctx.value.name : props.value;
|
|
4860
4957
|
const filterType = inCompound ? ctx.type : props.type;
|
|
@@ -4894,6 +4991,9 @@ function GroupRank(props) {
|
|
|
4894
4991
|
}
|
|
4895
4992
|
const jobs = useFrameworkGroups((s) => s.jobs);
|
|
4896
4993
|
const gangs = useFrameworkGroups((s) => s.gangs);
|
|
4994
|
+
react.useEffect(() => {
|
|
4995
|
+
ensureFrameworkGroups();
|
|
4996
|
+
}, []);
|
|
4897
4997
|
const all = [...jobs, ...gangs];
|
|
4898
4998
|
const selectedGroup = all.find((g) => g.name === ctx.value.name) ?? null;
|
|
4899
4999
|
const grades = selectedGroup?.grades ?? [];
|
|
@@ -6396,7 +6496,7 @@ var t2 = (key, fallback) => {
|
|
|
6396
6496
|
var FRAMEWORK_HINTS = {
|
|
6397
6497
|
"es_extended": "from ESX Config.Accounts",
|
|
6398
6498
|
"qb-core": "from QBCore Money.MoneyTypes",
|
|
6399
|
-
"qbx_core": "
|
|
6499
|
+
"qbx_core": "from QBox player session (or default trio if no one is online) \u2014 log in once to capture custom accounts",
|
|
6400
6500
|
"nd-framework": "ND_Core only supports cash + bank",
|
|
6401
6501
|
"unknown": "no framework detected \u2014 type account names manually"
|
|
6402
6502
|
};
|
|
@@ -6539,6 +6639,732 @@ function AccountSelect(props) {
|
|
|
6539
6639
|
!hideFrameworkHint && /* @__PURE__ */ jsxRuntime.jsx(FrameworkHint, { framework })
|
|
6540
6640
|
] });
|
|
6541
6641
|
}
|
|
6642
|
+
var BODY_HIDE_STYLE_ID = "dirk-instruction-panel-style";
|
|
6643
|
+
var BODY_HIDE_ATTR = "data-dirk-instruction-active";
|
|
6644
|
+
var OVERLAY_ATTR = "data-dirk-instruction-overlay";
|
|
6645
|
+
function ensureBodyHideStyle() {
|
|
6646
|
+
if (document.getElementById(BODY_HIDE_STYLE_ID)) return;
|
|
6647
|
+
const el = document.createElement("style");
|
|
6648
|
+
el.id = BODY_HIDE_STYLE_ID;
|
|
6649
|
+
el.textContent = `
|
|
6650
|
+
body[${BODY_HIDE_ATTR}] > *:not([${OVERLAY_ATTR}]) {
|
|
6651
|
+
visibility: hidden !important;
|
|
6652
|
+
opacity: 0 !important;
|
|
6653
|
+
pointer-events: none !important;
|
|
6654
|
+
}
|
|
6655
|
+
`;
|
|
6656
|
+
document.head.appendChild(el);
|
|
6657
|
+
}
|
|
6658
|
+
function InstructionPanel({
|
|
6659
|
+
visible,
|
|
6660
|
+
title,
|
|
6661
|
+
hint,
|
|
6662
|
+
keys,
|
|
6663
|
+
icon: Icon = lucideReact.MapPin,
|
|
6664
|
+
hideRestOfAdmin = true
|
|
6665
|
+
}) {
|
|
6666
|
+
const theme = core.useMantineTheme();
|
|
6667
|
+
const pc = theme.colors[theme.primaryColor];
|
|
6668
|
+
react.useEffect(() => {
|
|
6669
|
+
if (!visible || !hideRestOfAdmin) return;
|
|
6670
|
+
ensureBodyHideStyle();
|
|
6671
|
+
document.body.setAttribute(BODY_HIDE_ATTR, "");
|
|
6672
|
+
return () => {
|
|
6673
|
+
document.body.removeAttribute(BODY_HIDE_ATTR);
|
|
6674
|
+
};
|
|
6675
|
+
}, [visible, hideRestOfAdmin]);
|
|
6676
|
+
if (!visible) return null;
|
|
6677
|
+
return reactDom.createPortal(
|
|
6678
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6679
|
+
"div",
|
|
6680
|
+
{
|
|
6681
|
+
...{ [OVERLAY_ATTR]: "" },
|
|
6682
|
+
style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: 1e4 },
|
|
6683
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6684
|
+
framerMotion.motion.div,
|
|
6685
|
+
{
|
|
6686
|
+
initial: { opacity: 0, y: 12, scale: 0.92 },
|
|
6687
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
6688
|
+
exit: { opacity: 0, y: 12, scale: 0.92 },
|
|
6689
|
+
transition: { duration: 0.25, ease: "easeInOut" },
|
|
6690
|
+
style: { position: "absolute", bottom: "3vh", right: "3vh", userSelect: "none" },
|
|
6691
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6692
|
+
core.Flex,
|
|
6693
|
+
{
|
|
6694
|
+
direction: "column",
|
|
6695
|
+
gap: "0.8vh",
|
|
6696
|
+
style: {
|
|
6697
|
+
background: core.alpha(theme.colors.dark[9], 0.55),
|
|
6698
|
+
border: "0.1vh solid rgba(255,255,255,0.07)",
|
|
6699
|
+
borderRadius: theme.radius.sm,
|
|
6700
|
+
boxShadow: "0 0.74vh 2.96vh rgba(0,0,0,0.5)",
|
|
6701
|
+
padding: "1.4vh 1.6vh",
|
|
6702
|
+
minWidth: "22vh",
|
|
6703
|
+
maxWidth: "28vh"
|
|
6704
|
+
},
|
|
6705
|
+
children: [
|
|
6706
|
+
/* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", children: [
|
|
6707
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { size: "1.6vh", color: pc[6], strokeWidth: 2.5 }),
|
|
6708
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6709
|
+
core.Text,
|
|
6710
|
+
{
|
|
6711
|
+
style: {
|
|
6712
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6713
|
+
fontSize: "1.7vh",
|
|
6714
|
+
fontWeight: 700,
|
|
6715
|
+
letterSpacing: "0.14em",
|
|
6716
|
+
textTransform: "uppercase",
|
|
6717
|
+
color: pc[6],
|
|
6718
|
+
textShadow: `0 0 0.8vh ${core.alpha(pc[7], 0.5)}, 0 0 1.6vh ${core.alpha(pc[9], 0.3)}`
|
|
6719
|
+
},
|
|
6720
|
+
children: title
|
|
6721
|
+
}
|
|
6722
|
+
)
|
|
6723
|
+
] }),
|
|
6724
|
+
(hint || keys && keys.length > 0) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
|
|
6725
|
+
hint && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6726
|
+
core.Text,
|
|
6727
|
+
{
|
|
6728
|
+
style: {
|
|
6729
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6730
|
+
fontSize: "1.05vh",
|
|
6731
|
+
color: "rgba(255,255,255,0.45)",
|
|
6732
|
+
letterSpacing: "0.06em",
|
|
6733
|
+
textTransform: "uppercase",
|
|
6734
|
+
lineHeight: 1.4
|
|
6735
|
+
},
|
|
6736
|
+
children: hint
|
|
6737
|
+
}
|
|
6738
|
+
),
|
|
6739
|
+
keys && keys.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6740
|
+
hint && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "0.1vh", background: "rgba(255,255,255,0.06)", margin: "0.1vh 0" } }),
|
|
6741
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Flex, { direction: "column", gap: "0.5vh", children: keys.map((k, i) => /* @__PURE__ */ jsxRuntime.jsx(InstructionKeyRow, { keyLabel: k.key, action: k.action }, i)) })
|
|
6742
|
+
] })
|
|
6743
|
+
]
|
|
6744
|
+
}
|
|
6745
|
+
)
|
|
6746
|
+
},
|
|
6747
|
+
"instruction-card"
|
|
6748
|
+
) })
|
|
6749
|
+
}
|
|
6750
|
+
),
|
|
6751
|
+
document.body
|
|
6752
|
+
);
|
|
6753
|
+
}
|
|
6754
|
+
function renderKeyContent(keyLabel) {
|
|
6755
|
+
const normalized = keyLabel.trim().toUpperCase();
|
|
6756
|
+
if (normalized === "LMB") return /* @__PURE__ */ jsxRuntime.jsx(MouseIcon, { side: "left" });
|
|
6757
|
+
if (normalized === "RMB") return /* @__PURE__ */ jsxRuntime.jsx(MouseIcon, { side: "right" });
|
|
6758
|
+
if (normalized === "BACKSPACE" || keyLabel === "\u232B") return /* @__PURE__ */ jsxRuntime.jsx(BackspaceIcon, {});
|
|
6759
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6760
|
+
core.Text,
|
|
6761
|
+
{
|
|
6762
|
+
style: {
|
|
6763
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6764
|
+
fontSize: "1.2vh",
|
|
6765
|
+
color: "rgba(255,255,255,0.85)",
|
|
6766
|
+
lineHeight: 1,
|
|
6767
|
+
padding: "0 0.3vh"
|
|
6768
|
+
},
|
|
6769
|
+
children: keyLabel
|
|
6770
|
+
}
|
|
6771
|
+
);
|
|
6772
|
+
}
|
|
6773
|
+
function InstructionKeyRow({ keyLabel, action }) {
|
|
6774
|
+
const normalized = keyLabel.trim().toUpperCase();
|
|
6775
|
+
const isIconKey = normalized === "LMB" || normalized === "RMB" || normalized === "BACKSPACE" || keyLabel === "\u232B";
|
|
6776
|
+
const minWidth = isIconKey ? "2.6vh" : "2.4vh";
|
|
6777
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "0.6vh", children: [
|
|
6778
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6779
|
+
"div",
|
|
6780
|
+
{
|
|
6781
|
+
style: {
|
|
6782
|
+
minWidth,
|
|
6783
|
+
height: "2.4vh",
|
|
6784
|
+
padding: "0 0.4vh",
|
|
6785
|
+
borderRadius: "0.3vh",
|
|
6786
|
+
border: "0.15vh solid rgba(255,255,255,0.35)",
|
|
6787
|
+
background: "rgba(255,255,255,0.06)",
|
|
6788
|
+
display: "flex",
|
|
6789
|
+
alignItems: "center",
|
|
6790
|
+
justifyContent: "center",
|
|
6791
|
+
opacity: 0.85,
|
|
6792
|
+
filter: "drop-shadow(0 0 0.3vh rgba(0,0,0,0.5))",
|
|
6793
|
+
flexShrink: 0,
|
|
6794
|
+
boxSizing: "border-box"
|
|
6795
|
+
},
|
|
6796
|
+
children: renderKeyContent(keyLabel)
|
|
6797
|
+
}
|
|
6798
|
+
),
|
|
6799
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6800
|
+
core.Text,
|
|
6801
|
+
{
|
|
6802
|
+
style: {
|
|
6803
|
+
fontFamily: "Akrobat Bold, sans-serif",
|
|
6804
|
+
fontSize: "1.05vh",
|
|
6805
|
+
color: "rgba(255,255,255,0.45)",
|
|
6806
|
+
letterSpacing: "0.06em",
|
|
6807
|
+
textTransform: "uppercase"
|
|
6808
|
+
},
|
|
6809
|
+
children: action
|
|
6810
|
+
}
|
|
6811
|
+
)
|
|
6812
|
+
] });
|
|
6813
|
+
}
|
|
6814
|
+
function MouseIcon({ side }) {
|
|
6815
|
+
const stroke = "rgba(255,255,255,0.85)";
|
|
6816
|
+
const fillActive = "rgba(255,255,255,0.85)";
|
|
6817
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 16 22", width: "1.4vh", height: "1.9vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", children: [
|
|
6818
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "1", width: "14", height: "20", rx: "6" }),
|
|
6819
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "1", x2: "8", y2: "9" }),
|
|
6820
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6821
|
+
"path",
|
|
6822
|
+
{
|
|
6823
|
+
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",
|
|
6824
|
+
fill: fillActive,
|
|
6825
|
+
stroke: "none"
|
|
6826
|
+
}
|
|
6827
|
+
)
|
|
6828
|
+
] });
|
|
6829
|
+
}
|
|
6830
|
+
function BackspaceIcon() {
|
|
6831
|
+
const stroke = "rgba(255,255,255,0.85)";
|
|
6832
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 22 16", width: "1.7vh", height: "1.3vh", fill: "none", stroke, strokeWidth: "1.4", strokeLinejoin: "round", strokeLinecap: "round", children: [
|
|
6833
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 21 2 L 8 2 L 2 8 L 8 14 L 21 14 Z" }),
|
|
6834
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "11", y1: "6", x2: "16", y2: "11" }),
|
|
6835
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "6", x2: "11", y2: "11" })
|
|
6836
|
+
] });
|
|
6837
|
+
}
|
|
6838
|
+
function WorldPositionPicker({ value, onChange, compact, setOnly, gotoOnly }) {
|
|
6839
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6840
|
+
!gotoOnly && /* @__PURE__ */ jsxRuntime.jsx(WorldPositionSetButton2, { value, onChange, compact }),
|
|
6841
|
+
!setOnly && /* @__PURE__ */ jsxRuntime.jsx(WorldPositionGotoButton2, { value, compact })
|
|
6842
|
+
] });
|
|
6843
|
+
}
|
|
6844
|
+
function WorldPositionSetButton2({
|
|
6845
|
+
value,
|
|
6846
|
+
onChange,
|
|
6847
|
+
compact
|
|
6848
|
+
}) {
|
|
6849
|
+
const theme = core.useMantineTheme();
|
|
6850
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
6851
|
+
const begin = useAdminToolStore((s) => s.begin);
|
|
6852
|
+
const onClick = async () => {
|
|
6853
|
+
const instructions = {
|
|
6854
|
+
title: locale("PickPositionTitle") || "Pick Position",
|
|
6855
|
+
hint: locale("PickPositionHint") || "Walk to where you want this set",
|
|
6856
|
+
keys: [
|
|
6857
|
+
{ key: "E", action: locale("Set") || "Set" },
|
|
6858
|
+
{ key: "\u232B", action: locale("Cancel") || "Cancel" }
|
|
6859
|
+
]
|
|
6860
|
+
};
|
|
6861
|
+
const pendingResult = begin({ id: "capturePosition", ...instructions });
|
|
6862
|
+
fetchNui("ADMIN_TOOL_BEGIN", { id: "capturePosition", instructions }).catch(() => {
|
|
6863
|
+
useAdminToolStore.getState().cancelActive();
|
|
6864
|
+
});
|
|
6865
|
+
const result = await pendingResult;
|
|
6866
|
+
if (result) onChange(result);
|
|
6867
|
+
};
|
|
6868
|
+
return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: locale("SetWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6869
|
+
framerMotion.motion.button,
|
|
6870
|
+
{
|
|
6871
|
+
onClick,
|
|
6872
|
+
whileHover: { background: core.alpha(color, 0.18) },
|
|
6873
|
+
whileTap: { scale: 0.95 },
|
|
6874
|
+
style: {
|
|
6875
|
+
background: core.alpha(color, 0.1),
|
|
6876
|
+
border: `0.1vh solid ${core.alpha(color, 0.35)}`,
|
|
6877
|
+
borderRadius: theme.radius.xs,
|
|
6878
|
+
padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
|
|
6879
|
+
cursor: "pointer",
|
|
6880
|
+
display: "flex",
|
|
6881
|
+
alignItems: "center",
|
|
6882
|
+
gap: compact ? "0.3vh" : "0.4vh"
|
|
6883
|
+
},
|
|
6884
|
+
children: [
|
|
6885
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Crosshair, { size: compact ? "1.1vh" : "1.3vh", color }),
|
|
6886
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Set") })
|
|
6887
|
+
]
|
|
6888
|
+
}
|
|
6889
|
+
) });
|
|
6890
|
+
}
|
|
6891
|
+
function WorldPositionGotoButton2({
|
|
6892
|
+
value,
|
|
6893
|
+
compact
|
|
6894
|
+
}) {
|
|
6895
|
+
const theme = core.useMantineTheme();
|
|
6896
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
6897
|
+
const onClick = () => {
|
|
6898
|
+
fetchNui("ADMIN_TOOL_INVOKE", { id: "gotoCoord", value }).catch(() => {
|
|
6899
|
+
});
|
|
6900
|
+
};
|
|
6901
|
+
return /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { label: locale("GotoWorldTooltip"), position: "top", withArrow: true, withinPortal: true, zIndex: 2e3, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6902
|
+
framerMotion.motion.button,
|
|
6903
|
+
{
|
|
6904
|
+
onClick,
|
|
6905
|
+
whileHover: { background: core.alpha(color, 0.18) },
|
|
6906
|
+
whileTap: { scale: 0.95 },
|
|
6907
|
+
style: {
|
|
6908
|
+
background: core.alpha(color, 0.1),
|
|
6909
|
+
border: `0.1vh solid ${core.alpha(color, 0.35)}`,
|
|
6910
|
+
borderRadius: theme.radius.xs,
|
|
6911
|
+
padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
|
|
6912
|
+
cursor: "pointer",
|
|
6913
|
+
display: "flex",
|
|
6914
|
+
alignItems: "center",
|
|
6915
|
+
gap: compact ? "0.3vh" : "0.4vh"
|
|
6916
|
+
},
|
|
6917
|
+
children: [
|
|
6918
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { size: compact ? "1.1vh" : "1.3vh", color }),
|
|
6919
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: locale("Goto") })
|
|
6920
|
+
]
|
|
6921
|
+
}
|
|
6922
|
+
) });
|
|
6923
|
+
}
|
|
6924
|
+
function usePlayers(opts = {}) {
|
|
6925
|
+
const {
|
|
6926
|
+
includeOffline = false,
|
|
6927
|
+
search = "",
|
|
6928
|
+
limit = 50,
|
|
6929
|
+
staleTimeMs,
|
|
6930
|
+
refetchIntervalMs
|
|
6931
|
+
} = opts;
|
|
6932
|
+
const query = reactQuery.useQuery({
|
|
6933
|
+
queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
|
|
6934
|
+
queryFn: async () => {
|
|
6935
|
+
const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
|
|
6936
|
+
const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
|
|
6937
|
+
const result = await fetchNui(
|
|
6938
|
+
"ADMIN_TOOL_QUERY",
|
|
6939
|
+
payload,
|
|
6940
|
+
// Browser-dev fallback. Returns a couple of mock players so the
|
|
6941
|
+
// dev shell isn't blank.
|
|
6942
|
+
includeOffline ? [
|
|
6943
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
|
|
6944
|
+
{ id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
|
|
6945
|
+
] : [
|
|
6946
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
|
|
6947
|
+
]
|
|
6948
|
+
);
|
|
6949
|
+
return Array.isArray(result) ? result : [];
|
|
6950
|
+
},
|
|
6951
|
+
staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
|
|
6952
|
+
gcTime: 5 * 6e4,
|
|
6953
|
+
refetchInterval: refetchIntervalMs ?? false,
|
|
6954
|
+
refetchOnWindowFocus: false,
|
|
6955
|
+
placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
|
|
6956
|
+
});
|
|
6957
|
+
return {
|
|
6958
|
+
players: query.data ?? [],
|
|
6959
|
+
isLoading: query.isLoading,
|
|
6960
|
+
isFetching: query.isFetching,
|
|
6961
|
+
error: query.error ?? null,
|
|
6962
|
+
refresh: () => query.refetch()
|
|
6963
|
+
};
|
|
6964
|
+
}
|
|
6965
|
+
var DEBOUNCE_MS = 300;
|
|
6966
|
+
var ONLINE_COLOR = "#3FA83F";
|
|
6967
|
+
var OFFLINE_COLOR = "#E54141";
|
|
6968
|
+
function PlayerSelect({
|
|
6969
|
+
value,
|
|
6970
|
+
onChange,
|
|
6971
|
+
includeOffline = false,
|
|
6972
|
+
limit = 50,
|
|
6973
|
+
searchable: searchableProp,
|
|
6974
|
+
placeholder,
|
|
6975
|
+
nothingFoundMessage: nothingFoundMessageProp,
|
|
6976
|
+
loadingLabel = "Loading\u2026",
|
|
6977
|
+
onlineLabel = "Online",
|
|
6978
|
+
offlineLabel = "Offline",
|
|
6979
|
+
refreshLabel = "Refresh player list",
|
|
6980
|
+
...rest
|
|
6981
|
+
}) {
|
|
6982
|
+
const theme = core.useMantineTheme();
|
|
6983
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
6984
|
+
const [searchInput, setSearchInput] = react.useState("");
|
|
6985
|
+
const [debouncedSearch, setDebouncedSearch] = react.useState("");
|
|
6986
|
+
const { players, isFetching, refresh } = usePlayers({
|
|
6987
|
+
includeOffline,
|
|
6988
|
+
search: includeOffline ? debouncedSearch : void 0,
|
|
6989
|
+
limit
|
|
6990
|
+
});
|
|
6991
|
+
const sortedPlayers = react.useMemo(
|
|
6992
|
+
() => [...players].sort((a, b) => {
|
|
6993
|
+
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
6994
|
+
return a.charName.localeCompare(b.charName);
|
|
6995
|
+
}),
|
|
6996
|
+
[players]
|
|
6997
|
+
);
|
|
6998
|
+
const playerByCitizen = react.useMemo(() => {
|
|
6999
|
+
const m = /* @__PURE__ */ new Map();
|
|
7000
|
+
for (const p of sortedPlayers) m.set(p.citizenId, p);
|
|
7001
|
+
return m;
|
|
7002
|
+
}, [sortedPlayers]);
|
|
7003
|
+
const data = react.useMemo(() => {
|
|
7004
|
+
const items = sortedPlayers.map((p) => ({
|
|
7005
|
+
value: p.citizenId,
|
|
7006
|
+
label: formatLabel(p)
|
|
7007
|
+
}));
|
|
7008
|
+
if (value && !items.some((i) => i.value === value)) {
|
|
7009
|
+
items.unshift({ value, label: value });
|
|
7010
|
+
}
|
|
7011
|
+
return items;
|
|
7012
|
+
}, [sortedPlayers, value]);
|
|
7013
|
+
const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
|
|
7014
|
+
const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
|
|
7015
|
+
react.useEffect(() => {
|
|
7016
|
+
if (!includeOffline) return;
|
|
7017
|
+
if (selectedLabel && searchInput === selectedLabel) return;
|
|
7018
|
+
const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
|
|
7019
|
+
return () => clearTimeout(t3);
|
|
7020
|
+
}, [searchInput, includeOffline, selectedLabel]);
|
|
7021
|
+
const renderOption = ({ option }) => {
|
|
7022
|
+
const p = playerByCitizen.get(option.value);
|
|
7023
|
+
if (!p) return option.label;
|
|
7024
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
|
|
7025
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
|
|
7026
|
+
/* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
|
|
7027
|
+
] });
|
|
7028
|
+
};
|
|
7029
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7030
|
+
core.Select,
|
|
7031
|
+
{
|
|
7032
|
+
...rest,
|
|
7033
|
+
data,
|
|
7034
|
+
value: value ?? null,
|
|
7035
|
+
onChange: (v) => {
|
|
7036
|
+
if (!v) return onChange(null);
|
|
7037
|
+
const player = playerByCitizen.get(v) ?? null;
|
|
7038
|
+
onChange(player);
|
|
7039
|
+
},
|
|
7040
|
+
searchable: searchableProp ?? true,
|
|
7041
|
+
searchValue: includeOffline ? searchInput : void 0,
|
|
7042
|
+
onSearchChange: includeOffline ? setSearchInput : void 0,
|
|
7043
|
+
placeholder,
|
|
7044
|
+
nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
|
|
7045
|
+
maxDropdownHeight: 300,
|
|
7046
|
+
renderOption,
|
|
7047
|
+
leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
|
|
7048
|
+
rightSectionWidth: "3.5vh",
|
|
7049
|
+
rightSectionPointerEvents: "all",
|
|
7050
|
+
rightSection: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7051
|
+
core.ActionIcon,
|
|
7052
|
+
{
|
|
7053
|
+
variant: "subtle",
|
|
7054
|
+
size: "sm",
|
|
7055
|
+
onClick: (e) => {
|
|
7056
|
+
e.stopPropagation();
|
|
7057
|
+
refresh();
|
|
7058
|
+
},
|
|
7059
|
+
"aria-label": refreshLabel,
|
|
7060
|
+
title: refreshLabel,
|
|
7061
|
+
style: { marginRight: "0.6vh" },
|
|
7062
|
+
children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
|
|
7063
|
+
}
|
|
7064
|
+
)
|
|
7065
|
+
}
|
|
7066
|
+
);
|
|
7067
|
+
}
|
|
7068
|
+
function StatusDot({ online, onlineLabel, offlineLabel }) {
|
|
7069
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7070
|
+
"span",
|
|
7071
|
+
{
|
|
7072
|
+
title: online ? onlineLabel : offlineLabel,
|
|
7073
|
+
style: {
|
|
7074
|
+
display: "inline-block",
|
|
7075
|
+
width: "0.9vh",
|
|
7076
|
+
height: "0.9vh",
|
|
7077
|
+
borderRadius: "50%",
|
|
7078
|
+
backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
|
|
7079
|
+
boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
|
|
7080
|
+
flexShrink: 0
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
);
|
|
7084
|
+
}
|
|
7085
|
+
function formatLabel(p) {
|
|
7086
|
+
const parts = [p.charName || p.citizenId];
|
|
7087
|
+
if (p.online) {
|
|
7088
|
+
if (p.name) parts.push(p.name);
|
|
7089
|
+
if (p.id != null) parts.push(`#${p.id}`);
|
|
7090
|
+
}
|
|
7091
|
+
return parts.join(" \xB7 ");
|
|
7092
|
+
}
|
|
7093
|
+
function usePickDoor() {
|
|
7094
|
+
const begin = useAdminToolStore((s) => s.begin);
|
|
7095
|
+
return async () => {
|
|
7096
|
+
const pending = begin({
|
|
7097
|
+
id: "pickDoor",
|
|
7098
|
+
title: locale("PickDoorTitle"),
|
|
7099
|
+
hint: locale("PickDoorHint"),
|
|
7100
|
+
keys: [
|
|
7101
|
+
{ key: "LMB", action: locale("Toggle") },
|
|
7102
|
+
{ key: "E", action: locale("Confirm") },
|
|
7103
|
+
{ key: "BACKSPACE", action: locale("Cancel") }
|
|
7104
|
+
]
|
|
7105
|
+
});
|
|
7106
|
+
fetchNui("ADMIN_TOOL_BEGIN", { id: "pickDoor" }).catch(() => {
|
|
7107
|
+
useAdminToolStore.getState().cancelActive();
|
|
7108
|
+
});
|
|
7109
|
+
return await pending;
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
function DoorPickerButton({
|
|
7113
|
+
onPick,
|
|
7114
|
+
compact,
|
|
7115
|
+
label,
|
|
7116
|
+
tooltip
|
|
7117
|
+
}) {
|
|
7118
|
+
const theme = core.useMantineTheme();
|
|
7119
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
7120
|
+
const pickDoor = usePickDoor();
|
|
7121
|
+
const onClick = async () => {
|
|
7122
|
+
const picked = await pickDoor();
|
|
7123
|
+
if (picked && picked.doors.length > 0) onPick(picked);
|
|
7124
|
+
};
|
|
7125
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7126
|
+
core.Tooltip,
|
|
7127
|
+
{
|
|
7128
|
+
label: tooltip ?? locale("PickDoorTooltip"),
|
|
7129
|
+
position: "top",
|
|
7130
|
+
withArrow: true,
|
|
7131
|
+
withinPortal: true,
|
|
7132
|
+
zIndex: 2e3,
|
|
7133
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7134
|
+
framerMotion.motion.button,
|
|
7135
|
+
{
|
|
7136
|
+
onClick,
|
|
7137
|
+
whileHover: { background: core.alpha(color, 0.18) },
|
|
7138
|
+
whileTap: { scale: 0.95 },
|
|
7139
|
+
style: {
|
|
7140
|
+
background: core.alpha(color, 0.1),
|
|
7141
|
+
border: `0.1vh solid ${core.alpha(color, 0.35)}`,
|
|
7142
|
+
borderRadius: theme.radius.xs,
|
|
7143
|
+
padding: compact ? "0.25vh 0.6vh" : "0.5vh 0.8vh",
|
|
7144
|
+
cursor: "pointer",
|
|
7145
|
+
display: "flex",
|
|
7146
|
+
alignItems: "center",
|
|
7147
|
+
gap: compact ? "0.3vh" : "0.4vh"
|
|
7148
|
+
},
|
|
7149
|
+
children: [
|
|
7150
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.DoorOpen, { size: compact ? "1.1vh" : "1.3vh", color }),
|
|
7151
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: color, children: label ?? locale("PickDoor") })
|
|
7152
|
+
]
|
|
7153
|
+
}
|
|
7154
|
+
)
|
|
7155
|
+
}
|
|
7156
|
+
);
|
|
7157
|
+
}
|
|
7158
|
+
var MapImpl = react.memo(({
|
|
7159
|
+
children,
|
|
7160
|
+
initialZoom = 2,
|
|
7161
|
+
initialCenter = gameToMap(0, 0),
|
|
7162
|
+
mapStyle = "game"
|
|
7163
|
+
}) => {
|
|
7164
|
+
const mapRef = react.useRef(null);
|
|
7165
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7166
|
+
reactLeaflet.MapContainer,
|
|
7167
|
+
{
|
|
7168
|
+
maxBoundsViscosity: 1,
|
|
7169
|
+
preferCanvas: true,
|
|
7170
|
+
zoom: Math.round(initialZoom),
|
|
7171
|
+
zoomSnap: 1,
|
|
7172
|
+
zoomDelta: 1,
|
|
7173
|
+
zoomControl: false,
|
|
7174
|
+
crs: leaflet.CRS.Simple,
|
|
7175
|
+
style: {
|
|
7176
|
+
height: "100%",
|
|
7177
|
+
width: "100%",
|
|
7178
|
+
overflow: "hidden",
|
|
7179
|
+
outline: "none !important",
|
|
7180
|
+
border: "none !important",
|
|
7181
|
+
boxShadow: "none !important",
|
|
7182
|
+
backgroundColor: "#384950"
|
|
7183
|
+
},
|
|
7184
|
+
center: initialCenter,
|
|
7185
|
+
attributionControl: false,
|
|
7186
|
+
doubleClickZoom: false,
|
|
7187
|
+
inertia: false,
|
|
7188
|
+
zoomAnimation: false,
|
|
7189
|
+
ref: mapRef,
|
|
7190
|
+
maxBounds: [[-250, -250], [250, 250]],
|
|
7191
|
+
children: [
|
|
7192
|
+
/* @__PURE__ */ jsxRuntime.jsx(MapLayer, { mapLayer: mapStyle }),
|
|
7193
|
+
children
|
|
7194
|
+
]
|
|
7195
|
+
}
|
|
7196
|
+
);
|
|
7197
|
+
});
|
|
7198
|
+
MapImpl.displayName = "DirkMap";
|
|
7199
|
+
var Map2 = MapImpl;
|
|
7200
|
+
var MapLayer = ({ mapLayer }) => {
|
|
7201
|
+
const map = reactLeaflet.useMap();
|
|
7202
|
+
const layerRef = react.useRef(null);
|
|
7203
|
+
react.useEffect(() => {
|
|
7204
|
+
if (layerRef.current) {
|
|
7205
|
+
map.removeLayer(layerRef.current);
|
|
7206
|
+
}
|
|
7207
|
+
const layer = leaflet.tileLayer(
|
|
7208
|
+
`https://s.rsg.sc/sc/images/games/GTAV/map/${mapLayer}/{z}/{x}/{y}.jpg`,
|
|
7209
|
+
{
|
|
7210
|
+
maxZoom: 6,
|
|
7211
|
+
minZoom: 4,
|
|
7212
|
+
bounds: leaflet.latLngBounds(leaflet.latLng(0, 128), leaflet.latLng(-192, 0)),
|
|
7213
|
+
tileSize: 256,
|
|
7214
|
+
updateWhenZooming: false,
|
|
7215
|
+
keepBuffer: 2,
|
|
7216
|
+
opacity: 0.75
|
|
7217
|
+
}
|
|
7218
|
+
);
|
|
7219
|
+
layer.addTo(map);
|
|
7220
|
+
layerRef.current = layer;
|
|
7221
|
+
return () => {
|
|
7222
|
+
if (layerRef.current) {
|
|
7223
|
+
map.removeLayer(layerRef.current);
|
|
7224
|
+
}
|
|
7225
|
+
};
|
|
7226
|
+
}, [mapLayer, map]);
|
|
7227
|
+
return null;
|
|
7228
|
+
};
|
|
7229
|
+
function ZoomControls() {
|
|
7230
|
+
const theme = core.useMantineTheme();
|
|
7231
|
+
const map = reactLeaflet.useMap();
|
|
7232
|
+
const buttons = [
|
|
7233
|
+
{ Icon: lucideReact.Plus, fn: () => map.zoomIn() },
|
|
7234
|
+
{ Icon: lucideReact.Minus, fn: () => map.zoomOut() }
|
|
7235
|
+
];
|
|
7236
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7237
|
+
framerMotion.motion.div,
|
|
7238
|
+
{
|
|
7239
|
+
style: {
|
|
7240
|
+
position: "absolute",
|
|
7241
|
+
right: "2vh",
|
|
7242
|
+
top: "2vh",
|
|
7243
|
+
display: "flex",
|
|
7244
|
+
flexDirection: "column",
|
|
7245
|
+
zIndex: 999999,
|
|
7246
|
+
boxShadow: `0 0 1vh ${core.alpha(theme.colors.dark[9], 0.85)}`,
|
|
7247
|
+
background: core.alpha(theme.colors.dark[9], 0.85),
|
|
7248
|
+
borderRadius: theme.radius.xs
|
|
7249
|
+
},
|
|
7250
|
+
initial: { opacity: 0, y: -20 },
|
|
7251
|
+
animate: { opacity: 1, y: 0 },
|
|
7252
|
+
exit: { opacity: 0, y: -20 },
|
|
7253
|
+
children: buttons.map(({ Icon, fn }, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
7254
|
+
framerMotion.motion.div,
|
|
7255
|
+
{
|
|
7256
|
+
whileHover: { scale: 1.1, filter: "brightness(1.5)" },
|
|
7257
|
+
onClick: fn,
|
|
7258
|
+
style: {
|
|
7259
|
+
padding: theme.spacing.xs,
|
|
7260
|
+
cursor: "pointer",
|
|
7261
|
+
display: "flex"
|
|
7262
|
+
},
|
|
7263
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 34, color: theme.colors.gray[5] })
|
|
7264
|
+
},
|
|
7265
|
+
i
|
|
7266
|
+
))
|
|
7267
|
+
}
|
|
7268
|
+
);
|
|
7269
|
+
}
|
|
7270
|
+
var DEFAULT_SPRITE = 162;
|
|
7271
|
+
var DEFAULT_COLOR = 5;
|
|
7272
|
+
function BlipMarker({
|
|
7273
|
+
position,
|
|
7274
|
+
sprite,
|
|
7275
|
+
color,
|
|
7276
|
+
scale = 1,
|
|
7277
|
+
onClick,
|
|
7278
|
+
selected,
|
|
7279
|
+
disabled,
|
|
7280
|
+
fallbackSprite = DEFAULT_SPRITE,
|
|
7281
|
+
fallbackColor = DEFAULT_COLOR
|
|
7282
|
+
}) {
|
|
7283
|
+
const [hovered, setHovered] = react.useState(false);
|
|
7284
|
+
const mapCoords = react.useMemo(() => gameToMap(position.x, position.y), [position.x, position.y]);
|
|
7285
|
+
const effectiveSprite = sprite ?? fallbackSprite;
|
|
7286
|
+
const effectiveColor = color ?? fallbackColor;
|
|
7287
|
+
const url = blipUrlForSprite(effectiveSprite);
|
|
7288
|
+
const colorHex = getBlipColor(effectiveColor)?.hex ?? "#ffffff";
|
|
7289
|
+
const handleClick = (e) => {
|
|
7290
|
+
e.originalEvent?.stopPropagation?.();
|
|
7291
|
+
if (disabled) return;
|
|
7292
|
+
onClick?.();
|
|
7293
|
+
};
|
|
7294
|
+
const baseSize = 1.8 * scale;
|
|
7295
|
+
const size = `${baseSize}vh`;
|
|
7296
|
+
const ringSize = `${baseSize * 1.6}vh`;
|
|
7297
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7298
|
+
reactLeafletComponentMarker.Marker,
|
|
7299
|
+
{
|
|
7300
|
+
position: mapCoords,
|
|
7301
|
+
eventHandlers: onClick ? { click: handleClick } : void 0,
|
|
7302
|
+
icon: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7303
|
+
framerMotion.motion.div,
|
|
7304
|
+
{
|
|
7305
|
+
onHoverStart: () => setHovered(true),
|
|
7306
|
+
onHoverEnd: () => setHovered(false),
|
|
7307
|
+
style: {
|
|
7308
|
+
position: "relative",
|
|
7309
|
+
display: "flex",
|
|
7310
|
+
alignItems: "center",
|
|
7311
|
+
justifyContent: "center",
|
|
7312
|
+
cursor: disabled ? "not-allowed" : onClick ? "pointer" : "default",
|
|
7313
|
+
opacity: disabled ? 0.35 : 1,
|
|
7314
|
+
width: size,
|
|
7315
|
+
height: size
|
|
7316
|
+
},
|
|
7317
|
+
animate: { scale: hovered && !disabled ? 1.2 : selected ? 1.15 : 1 },
|
|
7318
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
7319
|
+
children: [
|
|
7320
|
+
(selected || hovered) && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7321
|
+
framerMotion.motion.div,
|
|
7322
|
+
{
|
|
7323
|
+
style: {
|
|
7324
|
+
position: "absolute",
|
|
7325
|
+
width: ringSize,
|
|
7326
|
+
height: ringSize,
|
|
7327
|
+
borderRadius: "50%",
|
|
7328
|
+
border: `0.18vh solid ${core.alpha(colorHex, 0.85)}`,
|
|
7329
|
+
boxShadow: `0 0 1vh ${core.alpha(colorHex, 0.55)}`
|
|
7330
|
+
},
|
|
7331
|
+
initial: { opacity: 0, scale: 0.8 },
|
|
7332
|
+
animate: { opacity: 1, scale: 1 }
|
|
7333
|
+
}
|
|
7334
|
+
),
|
|
7335
|
+
url && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7336
|
+
"div",
|
|
7337
|
+
{
|
|
7338
|
+
style: {
|
|
7339
|
+
width: size,
|
|
7340
|
+
height: size,
|
|
7341
|
+
backgroundColor: colorHex,
|
|
7342
|
+
WebkitMaskImage: `url(${url})`,
|
|
7343
|
+
maskImage: `url(${url})`,
|
|
7344
|
+
WebkitMaskRepeat: "no-repeat",
|
|
7345
|
+
maskRepeat: "no-repeat",
|
|
7346
|
+
WebkitMaskPosition: "center",
|
|
7347
|
+
maskPosition: "center",
|
|
7348
|
+
WebkitMaskSize: "contain",
|
|
7349
|
+
maskSize: "contain",
|
|
7350
|
+
filter: [
|
|
7351
|
+
"drop-shadow(0.12vh 0 0 #000)",
|
|
7352
|
+
"drop-shadow(-0.12vh 0 0 #000)",
|
|
7353
|
+
"drop-shadow(0 0.12vh 0 #000)",
|
|
7354
|
+
"drop-shadow(0 -0.12vh 0 #000)",
|
|
7355
|
+
`drop-shadow(0 0 0.4vh ${core.alpha("#000", 0.7)})`
|
|
7356
|
+
].join(" "),
|
|
7357
|
+
pointerEvents: "none",
|
|
7358
|
+
userSelect: "none"
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
)
|
|
7362
|
+
]
|
|
7363
|
+
}
|
|
7364
|
+
)
|
|
7365
|
+
}
|
|
7366
|
+
);
|
|
7367
|
+
}
|
|
6542
7368
|
|
|
6543
7369
|
exports.AccountSelect = AccountSelect;
|
|
6544
7370
|
exports.AdminPageTitle = AdminPageTitle;
|
|
@@ -6547,6 +7373,7 @@ exports.AsyncSaveButton = AsyncSaveButton;
|
|
|
6547
7373
|
exports.BlipColorSelect = BlipColorSelect;
|
|
6548
7374
|
exports.BlipDisplaySelect = BlipDisplaySelect;
|
|
6549
7375
|
exports.BlipIconSelect = BlipIconSelect;
|
|
7376
|
+
exports.BlipMarker = BlipMarker;
|
|
6550
7377
|
exports.BorderedIcon = BorderedIcon;
|
|
6551
7378
|
exports.ConfigPanel = ConfigPanel;
|
|
6552
7379
|
exports.ConfirmModal = ConfirmModal;
|
|
@@ -6554,6 +7381,7 @@ exports.ControlMultiSelect = ControlMultiSelect;
|
|
|
6554
7381
|
exports.ControlSelect = ControlSelect;
|
|
6555
7382
|
exports.Counter = Counter;
|
|
6556
7383
|
exports.DiscordRoleSelect = DiscordRoleSelect;
|
|
7384
|
+
exports.DoorPickerButton = DoorPickerButton;
|
|
6557
7385
|
exports.FiveMKeyBindInput = FiveMKeyBindInput;
|
|
6558
7386
|
exports.FloatingParticles = FloatingParticles;
|
|
6559
7387
|
exports.GroupName = GroupName;
|
|
@@ -6561,8 +7389,11 @@ exports.GroupRank = GroupRank;
|
|
|
6561
7389
|
exports.GroupSelect = GroupSelect;
|
|
6562
7390
|
exports.InfoBox = InfoBox;
|
|
6563
7391
|
exports.InputContainer = InputContainer;
|
|
7392
|
+
exports.InstructionPanel = InstructionPanel;
|
|
6564
7393
|
exports.LevelBanner = LevelBanner;
|
|
6565
7394
|
exports.LevelPanel = LevelPanel;
|
|
7395
|
+
exports.Map = Map2;
|
|
7396
|
+
exports.MapLayer = MapLayer;
|
|
6566
7397
|
exports.MissingItemsBanner = MissingItemsBanner;
|
|
6567
7398
|
exports.Modal = Modal;
|
|
6568
7399
|
exports.ModalContext = ModalContext;
|
|
@@ -6574,6 +7405,7 @@ exports.MotionText = MotionText;
|
|
|
6574
7405
|
exports.NavBar = NavBar;
|
|
6575
7406
|
exports.NavigationContext = NavigationContext;
|
|
6576
7407
|
exports.NavigationProvider = NavigationProvider;
|
|
7408
|
+
exports.PlayerSelect = PlayerSelect;
|
|
6577
7409
|
exports.PositionPicker = PositionPicker;
|
|
6578
7410
|
exports.PromptModal = PromptModal;
|
|
6579
7411
|
exports.ScenarioSelect = ScenarioSelect;
|
|
@@ -6587,11 +7419,19 @@ exports.Title = Title;
|
|
|
6587
7419
|
exports.Vector4DeleteButton = Vector4DeleteButton;
|
|
6588
7420
|
exports.Vector4Display = Vector4Display;
|
|
6589
7421
|
exports.WorldPositionGotoButton = WorldPositionGotoButton;
|
|
7422
|
+
exports.WorldPositionPicker = WorldPositionPicker;
|
|
6590
7423
|
exports.WorldPositionSetButton = WorldPositionSetButton;
|
|
7424
|
+
exports.ZoomControls = ZoomControls;
|
|
7425
|
+
exports.blipUrl = blipUrl;
|
|
7426
|
+
exports.blipUrlForSprite = blipUrlForSprite;
|
|
7427
|
+
exports.getBlipColor = getBlipColor;
|
|
7428
|
+
exports.getBlipEntry = getBlipEntry;
|
|
7429
|
+
exports.useAdminToolStore = useAdminToolStore;
|
|
6591
7430
|
exports.useMissingItemsAudit = useMissingItemsAudit;
|
|
6592
7431
|
exports.useModal = useModal;
|
|
6593
7432
|
exports.useModalActions = useModalActions;
|
|
6594
7433
|
exports.useNavigation = useNavigation;
|
|
6595
7434
|
exports.useNavigationStore = useNavigationStore;
|
|
7435
|
+
exports.usePickDoor = usePickDoor;
|
|
6596
7436
|
//# sourceMappingURL=index.cjs.map
|
|
6597
7437
|
//# sourceMappingURL=index.cjs.map
|