dirk-cfx-react 1.1.91 → 1.1.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1270,11 +1270,11 @@ var colorNames = {
1270
1270
  Yellow: { r: 255, g: 255, b: 0 },
1271
1271
  YellowGreen: { r: 154, g: 205, b: 50 }
1272
1272
  };
1273
- function colorWithAlpha(color, alpha18) {
1273
+ function colorWithAlpha(color, alpha19) {
1274
1274
  const lowerCasedColor = color.toLowerCase();
1275
1275
  if (colorNames[lowerCasedColor]) {
1276
1276
  const rgb = colorNames[lowerCasedColor];
1277
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
1277
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha19})`;
1278
1278
  }
1279
1279
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1280
1280
  const hex = color.slice(1);
@@ -1282,12 +1282,12 @@ function colorWithAlpha(color, alpha18) {
1282
1282
  const r = bigint >> 16 & 255;
1283
1283
  const g = bigint >> 8 & 255;
1284
1284
  const b = bigint & 255;
1285
- return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
1285
+ return `rgba(${r}, ${g}, ${b}, ${alpha19})`;
1286
1286
  }
1287
1287
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1288
1288
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1289
1289
  if (result) {
1290
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
1290
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha19})`;
1291
1291
  }
1292
1292
  }
1293
1293
  return color;
@@ -4687,6 +4687,16 @@ function mergeMantineThemeSafe(base, custom, override) {
4687
4687
  }
4688
4688
  };
4689
4689
  }
4690
+ function changedTopLevelSections(changedFields) {
4691
+ if (!changedFields || changedFields.length === 0) return [];
4692
+ const sections = /* @__PURE__ */ new Set();
4693
+ for (const path of changedFields) {
4694
+ if (typeof path !== "string" || path.length === 0) continue;
4695
+ const dot = path.indexOf(".");
4696
+ sections.add(dot === -1 ? path : path.slice(0, dot));
4697
+ }
4698
+ return Array.from(sections);
4699
+ }
4690
4700
  var _instance = null;
4691
4701
  function getScriptConfigInstance() {
4692
4702
  if (!_instance) throw new Error("[dirk-cfx-react] createScriptConfig must be called before using ConfigPanel");
@@ -4696,15 +4706,18 @@ function createScriptConfig(defaultValue) {
4696
4706
  const store2 = zustand.create(() => defaultValue);
4697
4707
  let clientVersion = 0;
4698
4708
  const useScriptConfigHooks = () => {
4699
- useNuiEvent("UPDATE_SCRIPT_CONFIG", (data) => {
4700
- if (!data) return;
4701
- if (typeof data.clientVersion === "number") {
4702
- clientVersion = data.clientVersion;
4703
- }
4704
- if (data.config && typeof data.config === "object") {
4705
- store2.setState((prev) => ({ ...prev, ...data.config }));
4709
+ useNuiEvent(
4710
+ "UPDATE_SCRIPT_CONFIG",
4711
+ (data) => {
4712
+ if (!data) return;
4713
+ if (typeof data.clientVersion === "number") {
4714
+ clientVersion = data.clientVersion;
4715
+ }
4716
+ if (data.config && typeof data.config === "object") {
4717
+ store2.setState((prev) => ({ ...prev, ...data.config }));
4718
+ }
4706
4719
  }
4707
- });
4720
+ );
4708
4721
  };
4709
4722
  const fetchScriptConfig = async () => {
4710
4723
  try {
@@ -4720,12 +4733,19 @@ function createScriptConfig(defaultValue) {
4720
4733
  }
4721
4734
  return null;
4722
4735
  };
4723
- const updateScriptConfig = async (newConfig) => {
4736
+ const updateScriptConfig = async (newConfig, changedFields) => {
4724
4737
  store2.setState((prev) => ({ ...prev, ...newConfig }));
4725
- const response = await fetchNui("UPDATE_SCRIPT_CONFIG", {
4726
- data: newConfig,
4727
- expectedVersion: clientVersion
4728
- });
4738
+ const sections = changedTopLevelSections(changedFields);
4739
+ let payload;
4740
+ if (sections.length > 0) {
4741
+ const current = store2.getState();
4742
+ const delta = {};
4743
+ for (const key of sections) delta[key] = current[key];
4744
+ payload = { data: delta, expectedVersion: clientVersion, sectionReplace: true };
4745
+ } else {
4746
+ payload = { data: newConfig, expectedVersion: clientVersion };
4747
+ }
4748
+ const response = await fetchNui("UPDATE_SCRIPT_CONFIG", payload);
4729
4749
  if (response?.meta?.client_version != null) {
4730
4750
  clientVersion = response.meta.client_version;
4731
4751
  }
@@ -5594,7 +5614,7 @@ function ConfigPanel(props) {
5594
5614
  if (isSaving) return;
5595
5615
  setIsSaving(true);
5596
5616
  try {
5597
- const result = await updateConfig(form.values);
5617
+ const result = await updateConfig(form.values, form.changedFields);
5598
5618
  if (result?.success) {
5599
5619
  form.reinitialize(cloneConfig(form.values));
5600
5620
  dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
@@ -7021,6 +7041,7 @@ function formatLabel(p) {
7021
7041
  }
7022
7042
  return parts.join(" \xB7 ");
7023
7043
  }
7044
+ var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
7024
7045
  var t = (key, fallback) => {
7025
7046
  const v = locale(key);
7026
7047
  return v === key ? fallback : v;
@@ -7043,7 +7064,6 @@ function AccessOverrideSection({
7043
7064
  schemaKey = "access",
7044
7065
  title,
7045
7066
  description: description2,
7046
- groupType,
7047
7067
  includeOffline = true
7048
7068
  }) {
7049
7069
  const mantineTheme = core.useMantineTheme();
@@ -7058,14 +7078,19 @@ function AccessOverrideSection({
7058
7078
  const [addingGroup, setAddingGroup] = React4.useState(false);
7059
7079
  const [addingPlayer, setAddingPlayer] = React4.useState(false);
7060
7080
  const commitGroup = (name) => {
7061
- if (!name) return;
7062
- set("groups", [...value.groups, name]);
7081
+ const g = name.trim();
7082
+ if (!g) return;
7083
+ if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
7063
7084
  setAddingGroup(false);
7064
7085
  };
7065
7086
  const setGroup = (index, name) => {
7066
7087
  const next = [...value.groups];
7067
7088
  next[index] = name;
7068
- set("groups", next.filter((g) => g !== ""));
7089
+ set("groups", next);
7090
+ };
7091
+ const quickAddGroup = (name) => {
7092
+ if (value.groups.includes(name)) return;
7093
+ set("groups", [...value.groups, name]);
7069
7094
  };
7070
7095
  const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
7071
7096
  const commitIdentifier = (id) => {
@@ -7097,19 +7122,68 @@ function AccessOverrideSection({
7097
7122
  ),
7098
7123
  /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description2 || t(
7099
7124
  "AccessOverrideDesc",
7100
- "Grant access to this resource's admin panel to specific groups and players, in addition to your global dirk_lib admins. Enforcement is server-side."
7125
+ "Grant access to this resource's admin panel to specific ACE groups and players, in addition to your global dirk_lib admins. Enforcement is server-side."
7101
7126
  ) }),
7102
- /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.Users, label: t("AccessGroups", "Groups") }),
7127
+ /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.Users, label: t("AccessGroups", "Admin groups (ACE)") }),
7128
+ /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { gap: "xs", wrap: "wrap", children: COMMON_ACE_GROUPS.map((g) => {
7129
+ const added = value.groups.includes(g);
7130
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7131
+ core.Flex,
7132
+ {
7133
+ align: "center",
7134
+ gap: "xxs",
7135
+ onClick: added ? void 0 : () => quickAddGroup(g),
7136
+ role: "button",
7137
+ tabIndex: added ? -1 : 0,
7138
+ "aria-disabled": added,
7139
+ onKeyDown: (e) => {
7140
+ if (added) return;
7141
+ if (e.key === "Enter" || e.key === " ") {
7142
+ e.preventDefault();
7143
+ quickAddGroup(g);
7144
+ }
7145
+ },
7146
+ px: "xs",
7147
+ py: "xxs",
7148
+ style: {
7149
+ cursor: added ? "default" : "pointer",
7150
+ opacity: added ? 0.4 : 1,
7151
+ background: core.alpha(color, 0.12),
7152
+ border: `0.1vh solid ${core.alpha(color, 0.4)}`,
7153
+ borderRadius: "0.4vh",
7154
+ transition: "background 0.15s, border-color 0.15s"
7155
+ },
7156
+ onMouseEnter: (e) => {
7157
+ if (added) return;
7158
+ e.currentTarget.style.background = core.alpha(color, 0.22);
7159
+ },
7160
+ onMouseLeave: (e) => {
7161
+ if (added) return;
7162
+ e.currentTarget.style.background = core.alpha(color, 0.12);
7163
+ },
7164
+ children: [
7165
+ !added && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: "1.2vh", color }),
7166
+ /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", lts: "0.04em", c: color, children: g })
7167
+ ]
7168
+ },
7169
+ g
7170
+ );
7171
+ }) }),
7103
7172
  /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "xxs", children: [
7104
- value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsxRuntime.jsx(EmptyHint, { text: t("AccessNoGroups", "No groups added.") }),
7173
+ value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsxRuntime.jsx(
7174
+ EmptyHint,
7175
+ {
7176
+ text: t("AccessNoGroups", "No ACE groups added. e.g. group.admin")
7177
+ }
7178
+ ),
7105
7179
  value.groups.map((name, i) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7106
7180
  /* @__PURE__ */ jsxRuntime.jsx(
7107
- GroupName,
7181
+ core.TextInput,
7108
7182
  {
7109
7183
  value: name,
7110
- onChange: (v) => setGroup(i, v),
7111
- type: groupType,
7112
- placeholder: t("AccessPickGroup", "Pick a group\u2026"),
7184
+ onChange: (e) => setGroup(i, e.currentTarget.value),
7185
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
7186
+ size: "xs",
7113
7187
  style: { flex: 1 }
7114
7188
  }
7115
7189
  ),
@@ -7126,30 +7200,15 @@ function AccessOverrideSection({
7126
7200
  }
7127
7201
  )
7128
7202
  ] }, i)),
7129
- addingGroup && /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7130
- /* @__PURE__ */ jsxRuntime.jsx(
7131
- GroupName,
7132
- {
7133
- value: "",
7134
- onChange: (v) => commitGroup(v),
7135
- type: groupType,
7136
- placeholder: t("AccessPickGroup", "Pick a group\u2026"),
7137
- style: { flex: 1 }
7138
- }
7139
- ),
7140
- /* @__PURE__ */ jsxRuntime.jsx(
7141
- core.ActionIcon,
7142
- {
7143
- variant: "subtle",
7144
- color: "red",
7145
- size: "md",
7146
- onClick: () => setAddingGroup(false),
7147
- title: t("Remove", "Remove"),
7148
- "aria-label": t("Remove", "Remove"),
7149
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
7150
- }
7151
- )
7152
- ] })
7203
+ addingGroup && /* @__PURE__ */ jsxRuntime.jsx(
7204
+ AceGroupAddRow,
7205
+ {
7206
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
7207
+ onCommit: commitGroup,
7208
+ onCancel: () => setAddingGroup(false),
7209
+ removeLabel: t("Remove", "Remove")
7210
+ }
7211
+ )
7153
7212
  ] }),
7154
7213
  !addingGroup && /* @__PURE__ */ jsxRuntime.jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
7155
7214
  /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.UserRound, label: t("AccessPlayers", "Players") }),
@@ -7249,6 +7308,53 @@ function AddRowButton({ label: label2, onClick }) {
7249
7308
  }
7250
7309
  );
7251
7310
  }
7311
+ function AceGroupAddRow({
7312
+ placeholder,
7313
+ onCommit,
7314
+ onCancel,
7315
+ removeLabel
7316
+ }) {
7317
+ const [draft, setDraft] = React4.useState("");
7318
+ const commit = () => {
7319
+ if (draft.trim()) onCommit(draft);
7320
+ else onCancel();
7321
+ };
7322
+ return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
7323
+ /* @__PURE__ */ jsxRuntime.jsx(
7324
+ core.TextInput,
7325
+ {
7326
+ value: draft,
7327
+ onChange: (e) => setDraft(e.currentTarget.value),
7328
+ onKeyDown: (e) => {
7329
+ if (e.key === "Enter") {
7330
+ e.preventDefault();
7331
+ commit();
7332
+ } else if (e.key === "Escape") {
7333
+ e.preventDefault();
7334
+ onCancel();
7335
+ }
7336
+ },
7337
+ onBlur: commit,
7338
+ placeholder,
7339
+ size: "xs",
7340
+ autoFocus: true,
7341
+ style: { flex: 1 }
7342
+ }
7343
+ ),
7344
+ /* @__PURE__ */ jsxRuntime.jsx(
7345
+ core.ActionIcon,
7346
+ {
7347
+ variant: "subtle",
7348
+ color: "red",
7349
+ size: "md",
7350
+ onClick: onCancel,
7351
+ title: removeLabel,
7352
+ "aria-label": removeLabel,
7353
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
7354
+ }
7355
+ )
7356
+ ] });
7357
+ }
7252
7358
 
7253
7359
  // src/utils/gtaAnimPostFx.ts
7254
7360
  var GTA_ANIM_POST_FX_GROUP_ORDER = [