@bizapps/ui 1.4.2 → 1.5.1

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/main.cjs CHANGED
@@ -5122,6 +5122,8 @@ function NavigationMenuIndicator({
5122
5122
  }
5123
5123
  );
5124
5124
  }
5125
+
5126
+ // src/components/authorization/policy.ts
5125
5127
  function matchPermission(pattern, permission) {
5126
5128
  if (pattern === "*") return true;
5127
5129
  if (pattern === permission) return true;
@@ -5137,41 +5139,46 @@ function matchesAny(permissionSet, permission) {
5137
5139
  }
5138
5140
  return false;
5139
5141
  }
5142
+ function can(permissionSet, perms, mode = "any") {
5143
+ const list = Array.isArray(perms) ? perms : [perms];
5144
+ if (list.length === 0) return false;
5145
+ if (permissionSet.has("*")) return true;
5146
+ if (mode === "all") {
5147
+ return list.every((p) => matchesAny(permissionSet, p));
5148
+ }
5149
+ return list.some((p) => matchesAny(permissionSet, p));
5150
+ }
5140
5151
  var AbilityContext = react.createContext(void 0);
5141
5152
  AbilityContext.displayName = "AbilityContext";
5142
5153
  function AbilityProvider({ children, permissions }) {
5143
5154
  const [permissionSet, setPermissionSet] = react.useState(
5144
5155
  () => new Set(permissions ?? [])
5145
5156
  );
5146
- const can = react.useCallback(
5147
- (perms, mode = "any") => {
5148
- const list = Array.isArray(perms) ? perms : [perms];
5149
- if (list.length === 0) return false;
5150
- if (permissionSet.has("*")) return true;
5151
- if (mode === "all") {
5152
- return list.every((p) => matchesAny(permissionSet, p));
5153
- }
5154
- return list.some((p) => matchesAny(permissionSet, p));
5155
- },
5157
+ const [imperativelyResolved, setImperativelyResolved] = react.useState(false);
5158
+ const status = permissions !== void 0 || imperativelyResolved ? "ready" : "loading";
5159
+ const can2 = react.useCallback(
5160
+ (perms, mode = "any") => can(permissionSet, perms, mode),
5156
5161
  [permissionSet]
5157
5162
  );
5158
5163
  const setAbilities = react.useCallback((perms) => {
5159
5164
  setPermissionSet(new Set(perms));
5165
+ setImperativelyResolved(true);
5160
5166
  }, []);
5161
5167
  const clearAbilities = react.useCallback(() => {
5162
5168
  setPermissionSet(/* @__PURE__ */ new Set());
5169
+ setImperativelyResolved(true);
5163
5170
  }, []);
5164
- const syncedKey = react.useRef(permissions ? permissions.join("|") : null);
5171
+ const permissionKey = permissions === void 0 ? null : [...permissions].sort().join("|");
5172
+ const syncedKey = react.useRef(permissionKey);
5165
5173
  react.useEffect(() => {
5166
- if (permissions === void 0) return;
5167
- const key = permissions.join("|");
5168
- if (key === syncedKey.current) return;
5169
- syncedKey.current = key;
5174
+ if (permissionKey === null) return;
5175
+ if (permissionKey === syncedKey.current) return;
5176
+ syncedKey.current = permissionKey;
5170
5177
  setPermissionSet(new Set(permissions));
5171
- }, [permissions]);
5178
+ }, [permissionKey, permissions]);
5172
5179
  const value = react.useMemo(
5173
- () => ({ can, setAbilities, clearAbilities }),
5174
- [can, setAbilities, clearAbilities]
5180
+ () => ({ can: can2, setAbilities, clearAbilities, status }),
5181
+ [can2, setAbilities, clearAbilities, status]
5175
5182
  );
5176
5183
  return /* @__PURE__ */ jsxRuntime.jsx(AbilityContext.Provider, { value, children });
5177
5184
  }
@@ -5183,21 +5190,176 @@ function useAbility() {
5183
5190
  return context;
5184
5191
  }
5185
5192
  function useCan(permissions, mode = "any") {
5186
- const { can } = useAbility();
5187
- return can(permissions, mode);
5193
+ const { can: can2 } = useAbility();
5194
+ return can2(permissions, mode);
5195
+ }
5196
+ function useAbilityStatus() {
5197
+ return useAbility().status;
5198
+ }
5199
+
5200
+ // src/components/authorization/permissions.generated.ts
5201
+ var KNOWN_PERMISSIONS = [];
5202
+
5203
+ // src/components/authorization/permission-catalog.ts
5204
+ var CATALOG = new Set(KNOWN_PERMISSIONS);
5205
+ var warned = /* @__PURE__ */ new Set();
5206
+ function warnUnknownPermission(permission) {
5207
+ if (process.env.NODE_ENV === "production") return;
5208
+ if (CATALOG.size === 0) return;
5209
+ const requested = Array.isArray(permission) ? permission : [permission];
5210
+ for (const entry of requested) {
5211
+ if (entry.includes("*")) continue;
5212
+ if (CATALOG.has(entry) || warned.has(entry)) continue;
5213
+ warned.add(entry);
5214
+ console.warn(
5215
+ `[Can] Unknown permission "${entry}". It is not in the generated permission catalog. Check for a typo, or regenerate the catalog with \`pnpm generate-permissions\`.`
5216
+ );
5217
+ }
5218
+ }
5219
+ var FOCUS_TRAVERSAL_KEYS = /* @__PURE__ */ new Set(["Tab", "Escape"]);
5220
+ function blockInteraction(event) {
5221
+ event.preventDefault();
5222
+ event.stopPropagation();
5223
+ }
5224
+ function blockNonTraversalKey(event) {
5225
+ if (!FOCUS_TRAVERSAL_KEYS.has(event.key)) blockInteraction(event);
5226
+ }
5227
+ var warnedRenderPropMisuse = /* @__PURE__ */ new Set();
5228
+ function warnRenderPropVoidsProps(voided) {
5229
+ if (process.env.NODE_ENV === "production") return;
5230
+ if (voided.length === 0) return;
5231
+ const key = voided.join(",");
5232
+ if (warnedRenderPropMisuse.has(key)) return;
5233
+ warnedRenderPropMisuse.add(key);
5234
+ console.warn(
5235
+ `[Can] \`children\` is a function, so ${voided.map((prop) => `\`${prop}\``).join(", ")} ${voided.length === 1 ? "is" : "are"} ignored. A render prop owns every state: read \`allowed\` and \`status\` and render the disabled, loading and denied cases yourself, or pass an element instead of a function.`
5236
+ );
5237
+ }
5238
+ function disableElement(child) {
5239
+ return react.cloneElement(child, {
5240
+ "aria-disabled": "true",
5241
+ className: cn(child.props.className, "cursor-not-allowed opacity-50"),
5242
+ onClickCapture: blockInteraction,
5243
+ onKeyDownCapture: blockNonTraversalKey,
5244
+ onKeyUpCapture: blockNonTraversalKey,
5245
+ onPointerDownCapture: blockInteraction,
5246
+ onMouseDownCapture: blockInteraction
5247
+ });
5248
+ }
5249
+ function InteractionFirewall({ children }) {
5250
+ return /* @__PURE__ */ jsxRuntime.jsx(
5251
+ "span",
5252
+ {
5253
+ "data-slot": "can-blocked",
5254
+ className: "contents",
5255
+ onClickCapture: blockInteraction,
5256
+ onPointerDownCapture: blockInteraction,
5257
+ onMouseDownCapture: blockInteraction,
5258
+ onKeyDownCapture: blockNonTraversalKey,
5259
+ onKeyUpCapture: blockNonTraversalKey,
5260
+ children
5261
+ }
5262
+ );
5263
+ }
5264
+ function DisabledSubtree({
5265
+ children,
5266
+ className,
5267
+ ...props
5268
+ }) {
5269
+ const { "data-slot": _incomingDataSlot, ...restProps } = props;
5270
+ return /* @__PURE__ */ jsxRuntime.jsx(
5271
+ "span",
5272
+ {
5273
+ "data-slot": "can-disabled",
5274
+ "aria-disabled": "true",
5275
+ className: cn(
5276
+ "inline-flex cursor-not-allowed opacity-50",
5277
+ "*:pointer-events-none",
5278
+ className
5279
+ ),
5280
+ onClickCapture: blockInteraction,
5281
+ onPointerDownCapture: blockInteraction,
5282
+ onMouseDownCapture: blockInteraction,
5283
+ onKeyDownCapture: blockNonTraversalKey,
5284
+ onKeyUpCapture: blockNonTraversalKey,
5285
+ ...restProps,
5286
+ children
5287
+ }
5288
+ );
5188
5289
  }
5189
- function Can({ permission, mode = "any", negate = false, fallback = null, children }) {
5190
- const { can } = useAbility();
5191
- const hasPermission = can(permission, mode);
5290
+ function Can({
5291
+ permission,
5292
+ mode = "any",
5293
+ negate = false,
5294
+ behavior = "hide",
5295
+ reason = null,
5296
+ fallback = null,
5297
+ loading,
5298
+ children
5299
+ }) {
5300
+ warnUnknownPermission(permission);
5301
+ const { can: can2, status } = useAbility();
5302
+ const hasPermission = status === "ready" && can2(permission, mode);
5192
5303
  const show = negate ? !hasPermission : hasPermission;
5193
5304
  if (typeof children === "function") {
5194
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(show) });
5305
+ warnRenderPropVoidsProps([
5306
+ ...behavior === "disable" ? ["behavior='disable'"] : [],
5307
+ ...reason !== null ? ["reason"] : [],
5308
+ ...fallback !== null ? ["fallback"] : [],
5309
+ ...loading !== void 0 ? ["loading"] : []
5310
+ ]);
5311
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children({ allowed: show, status }) });
5312
+ }
5313
+ if (status === "loading" && loading !== void 0) {
5314
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loading });
5315
+ }
5316
+ if (show) {
5317
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
5195
5318
  }
5196
- return show ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
5319
+ if (behavior === "disable") {
5320
+ if (react.isValidElement(children) && children.type !== react.Fragment) {
5321
+ const disabledChild = disableElement(children);
5322
+ if (reason === null) {
5323
+ return /* @__PURE__ */ jsxRuntime.jsx(InteractionFirewall, { children: disabledChild });
5324
+ }
5325
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5326
+ /* @__PURE__ */ jsxRuntime.jsx(InteractionFirewall, { children: /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: disabledChild }) }),
5327
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: reason })
5328
+ ] });
5329
+ }
5330
+ if (reason === null) {
5331
+ return /* @__PURE__ */ jsxRuntime.jsx(DisabledSubtree, { children });
5332
+ }
5333
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5334
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(DisabledSubtree, { children }) }),
5335
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: reason })
5336
+ ] });
5337
+ }
5338
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
5197
5339
  }
5198
5340
  Can.displayName = "Can";
5199
- function CanNot({ permission, mode = "any", fallback = null, children }) {
5200
- return /* @__PURE__ */ jsxRuntime.jsx(Can, { permission, mode, negate: true, fallback, children });
5341
+ function CanNot({
5342
+ permission,
5343
+ mode = "any",
5344
+ behavior = "hide",
5345
+ reason = null,
5346
+ fallback = null,
5347
+ loading,
5348
+ children
5349
+ }) {
5350
+ return /* @__PURE__ */ jsxRuntime.jsx(
5351
+ Can,
5352
+ {
5353
+ permission,
5354
+ mode,
5355
+ negate: true,
5356
+ behavior,
5357
+ reason,
5358
+ fallback,
5359
+ loading,
5360
+ children
5361
+ }
5362
+ );
5201
5363
  }
5202
5364
  CanNot.displayName = "CanNot";
5203
5365
  function useMediaQuery(query) {
@@ -9367,6 +9529,7 @@ exports.ItemHeader = ItemHeader;
9367
9529
  exports.ItemMedia = ItemMedia;
9368
9530
  exports.ItemSeparator = ItemSeparator;
9369
9531
  exports.ItemTitle = ItemTitle;
9532
+ exports.KNOWN_PERMISSIONS = KNOWN_PERMISSIONS;
9370
9533
  exports.Label = Label;
9371
9534
  exports.Link = Link;
9372
9535
  exports.Logo = Logo;
@@ -9527,6 +9690,7 @@ exports.toggleVariants = toggleVariants;
9527
9690
  exports.tooltipContentVariants = tooltipContentVariants;
9528
9691
  exports.transition = transition;
9529
9692
  exports.useAbility = useAbility;
9693
+ exports.useAbilityStatus = useAbilityStatus;
9530
9694
  exports.useAppForm = useAppForm;
9531
9695
  exports.useCan = useCan;
9532
9696
  exports.useCarousel = useCarousel;