@casl/react 6.0.0 → 7.0.0-rc
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/README.md +64 -118
- package/dist/cjs/index.cjs +49 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/esm/index.mjs +30 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/types/Can.d.ts +8 -24
- package/dist/types/hooks/useAbility.d.ts +6 -3
- package/dist/types/index.d.ts +0 -1
- package/package.json +12 -16
- package/dist/es5m/index.js +0 -2
- package/dist/es5m/index.js.map +0 -1
- package/dist/es6m/index.mjs +0 -2
- package/dist/es6m/index.mjs.map +0 -1
- package/dist/types/factory.d.ts +0 -4
- package/dist/umd/index.js +0 -2
- package/dist/umd/index.js.map +0 -1
- package/index.d.ts +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/%40casl%2Freact)
|
|
5
5
|
[](https://github.com/stalniy/casl/discussions)
|
|
6
6
|
|
|
7
|
-
This package
|
|
7
|
+
This package integrates `@casl/ability` with [React]. It provides:
|
|
8
|
+
|
|
9
|
+
* `AbilityProvider` to expose the current `Ability` instance through React context
|
|
10
|
+
* declarative `<Can>` component for conditional rendering
|
|
11
|
+
* `useAbility` hook for imperative checks that stay in sync with ability updates
|
|
8
12
|
|
|
9
13
|
> `@casl/react` perfectly works with [React Native](https://reactnative.dev/)
|
|
10
14
|
|
|
@@ -18,8 +22,42 @@ yarn add @casl/react @casl/ability
|
|
|
18
22
|
pnpm add @casl/react @casl/ability
|
|
19
23
|
```
|
|
20
24
|
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { createMongoAbility } from '@casl/ability';
|
|
29
|
+
import { AbilityProvider, Can, useAbility } from '@casl/react';
|
|
30
|
+
|
|
31
|
+
const ability = createMongoAbility([
|
|
32
|
+
{ action: 'read', subject: 'Post' },
|
|
33
|
+
{ action: 'create', subject: 'Post' },
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
export function App() {
|
|
37
|
+
return (
|
|
38
|
+
<AbilityProvider value={ability}>
|
|
39
|
+
<Can I="read" a="Post">
|
|
40
|
+
<div>List of posts</div>
|
|
41
|
+
</Can>
|
|
42
|
+
<CreatePostButton />
|
|
43
|
+
</AbilityProvider>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function CreatePostButton() {
|
|
48
|
+
const ability = useAbility();
|
|
49
|
+
return ability.can('create', 'Post') && (
|
|
50
|
+
<button>Create Post</button>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use `<Can>` for straightforward conditional rendering in JSX and `useAbility()` when the permission check is part of more complex component logic.
|
|
56
|
+
|
|
21
57
|
## Can component
|
|
22
58
|
|
|
59
|
+
`<Can>` reads the current `Ability` instance from `AbilityProvider`, re-renders when rules change, and memoizes the relevant rule lookup until the ability, rules, or relevant props change.
|
|
60
|
+
|
|
23
61
|
It accepts children and 6 properties:
|
|
24
62
|
|
|
25
63
|
* `do` - name of the action (e.g., `read`, `update`). Has an alias `I`
|
|
@@ -45,71 +83,48 @@ It accepts children and 6 properties:
|
|
|
45
83
|
```jsx
|
|
46
84
|
export default () => (
|
|
47
85
|
<Can I="create" a="Post" passThrough>
|
|
48
|
-
{
|
|
86
|
+
{({ isAllowed, reason }) => (
|
|
87
|
+
<button disabled={!isAllowed} title={reason}>Save</button>
|
|
88
|
+
)}
|
|
49
89
|
</Can>
|
|
50
90
|
)
|
|
51
91
|
```
|
|
52
92
|
|
|
53
|
-
* `
|
|
54
|
-
* `children` - elements to hide or render. May be either a render function:
|
|
93
|
+
* `children` - elements to hide or render. May be either a render function that receives `{ isAllowed, ability, reason }`:
|
|
55
94
|
|
|
56
95
|
```jsx
|
|
57
|
-
export default () => <Can I="create" a="Post"
|
|
58
|
-
{(
|
|
96
|
+
export default () => <Can I="create" a="Post">
|
|
97
|
+
{({ isAllowed, reason }) => (
|
|
98
|
+
<button disabled={!isAllowed} title={reason}>Create Post</button>
|
|
99
|
+
)}
|
|
59
100
|
</Can>
|
|
60
101
|
```
|
|
61
102
|
|
|
62
103
|
or React elements:
|
|
63
104
|
|
|
64
105
|
```jsx
|
|
65
|
-
export default () => <Can I="create" a="Post"
|
|
106
|
+
export default () => <Can I="create" a="Post">
|
|
66
107
|
<button onClick={this.createPost}>Create Post</button>
|
|
67
108
|
</Can>
|
|
68
109
|
```
|
|
69
110
|
|
|
70
111
|
> it's better to pass children as a render function because it will not create additional React elements if user doesn't have ability to do some action (in the case above `create Post`)
|
|
71
112
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Bind Can to a particular Ability instance
|
|
75
|
-
|
|
76
|
-
It'd be inconvenient to pass `ability` in every `Can` component. That's why there are 2 function which allow to bind `Can` to use a particular instance of `Ability`:
|
|
113
|
+
For simple visibility guards, `<Can>` keeps JSX readable. For more complex conditions, composing several checks, or passing authorization state deeper into your component tree, prefer `useAbility`.
|
|
77
114
|
|
|
78
|
-
|
|
79
|
-
This function was created to support version of React < 16.4.0, those versions doesn't have [Context API][react-ctx-api]. Can be used like this:
|
|
115
|
+
### Provide Ability instance
|
|
80
116
|
|
|
81
|
-
|
|
82
|
-
import { createCanBoundTo } from '@casl/react';
|
|
83
|
-
import ability from './ability';
|
|
84
|
-
|
|
85
|
-
export const Can = createCanBoundTo(ability);
|
|
86
|
-
```
|
|
87
|
-
* `createContextualCan`\
|
|
88
|
-
This function is created to support [React's Context API][react-ctx-api] and can be used like this:
|
|
89
|
-
|
|
90
|
-
```js @{data-filename="Can.js"}
|
|
91
|
-
import { createContext } from 'react';
|
|
92
|
-
import { createContextualCan } from '@casl/react';
|
|
93
|
-
|
|
94
|
-
export const AbilityContext = createContext();
|
|
95
|
-
export const Can = createContextualCan(AbilityContext.Consumer);
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
The 2 methods are almost the same, the 2nd one is slightly better because it will allow you to provide different `Ability` instances to different parts of your app and inject ability using [`contextType` static property](https://reactjs.org/docs/context.html#classcontexttype). Choose your way based on the version of React you use.
|
|
99
|
-
|
|
100
|
-
> In this guide, we will use `createContextualCan` as it covers more cases in modern React development.
|
|
101
|
-
|
|
102
|
-
To finalize things, we need to provide an instance of `Ability` via `AbilityContext.Provider`:
|
|
117
|
+
Wrap the part of your app that needs authorization checks with `AbilityProvider`:
|
|
103
118
|
|
|
104
119
|
```jsx @{data-filename="App.jsx"}
|
|
105
|
-
import {
|
|
106
|
-
import ability from './ability'
|
|
120
|
+
import { AbilityProvider } from '@casl/react';
|
|
121
|
+
import ability from './ability';
|
|
107
122
|
|
|
108
|
-
export default function App(
|
|
123
|
+
export default function App() {
|
|
109
124
|
return (
|
|
110
|
-
<
|
|
125
|
+
<AbilityProvider ability={ability}>
|
|
111
126
|
<TodoApp />
|
|
112
|
-
</
|
|
127
|
+
</AbilityProvider>
|
|
113
128
|
)
|
|
114
129
|
}
|
|
115
130
|
```
|
|
@@ -120,7 +135,7 @@ and use our `Can` component:
|
|
|
120
135
|
|
|
121
136
|
```jsx
|
|
122
137
|
import React, { Component } from 'react'
|
|
123
|
-
import { Can } from '
|
|
138
|
+
import { Can } from '@casl/react'
|
|
124
139
|
|
|
125
140
|
export class TodoApp extends Component {
|
|
126
141
|
createTodo = () => {
|
|
@@ -139,60 +154,14 @@ export class TodoApp extends Component {
|
|
|
139
154
|
|
|
140
155
|
### Imperative access to Ability instance
|
|
141
156
|
|
|
142
|
-
Sometimes the logic in a component
|
|
143
|
-
|
|
144
|
-
```jsx
|
|
145
|
-
import React, { Component } from 'react'
|
|
146
|
-
import { AbilityContext } from './Can'
|
|
147
|
-
|
|
148
|
-
export class TodoApp extends Component {
|
|
149
|
-
createTodo = () => {
|
|
150
|
-
// logic to show new todo form
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
render() {
|
|
154
|
-
return (
|
|
155
|
-
<div>
|
|
156
|
-
{this.context.can('create', 'Todo') &&
|
|
157
|
-
<button onClick={this.createTodo}>Create Todo</button>}
|
|
158
|
-
</div>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
TodoApp.contextType = AbilityContext;
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
or `useContext` hook:
|
|
167
|
-
|
|
168
|
-
```jsx
|
|
169
|
-
import React, { useContext } from 'react';
|
|
170
|
-
import { AbilityContext } from './Can'
|
|
171
|
-
|
|
172
|
-
export default () => {
|
|
173
|
-
const createTodo = () => { /* logic to show new todo form */ };
|
|
174
|
-
const ability = useContext(AbilityContext);
|
|
175
|
-
|
|
176
|
-
return (
|
|
177
|
-
<div>
|
|
178
|
-
{ability.can('create', 'Todo') &&
|
|
179
|
-
<button onClick={createTodo}>Create Todo</button>}
|
|
180
|
-
</div>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
In that case, you need to create a new `Ability` instance when you want to update user permissions (don't use `update` method, it won't trigger re-rendering in this case) or you need to force re-render the whole app.
|
|
186
|
-
|
|
187
|
-
To make things easier, `@casl/react` provides `useAbility` hook that accepts `React.Context` as the only argument (the same as `useContext`), but triggers re-render in the component where you use this hook when you update `Ability` rules. The example above can be rewritten to:
|
|
157
|
+
Sometimes the logic in a component is more complex than a simple visibility guard. In such cases, use `useAbility`. It reads the current ability from `AbilityProvider` and re-renders the component when ability rules change:
|
|
188
158
|
|
|
189
159
|
```jsx
|
|
190
160
|
import { useAbility } from '@casl/react';
|
|
191
|
-
import { AbilityContext } from './Can'
|
|
192
161
|
|
|
193
162
|
export default () => {
|
|
194
163
|
const createTodo = () => { /* logic to show new todo form */ };
|
|
195
|
-
const ability = useAbility(
|
|
164
|
+
const ability = useAbility();
|
|
196
165
|
|
|
197
166
|
return (
|
|
198
167
|
<div>
|
|
@@ -203,28 +172,6 @@ export default () => {
|
|
|
203
172
|
}
|
|
204
173
|
```
|
|
205
174
|
|
|
206
|
-
### Usage note on React < 16.4 with TypeScript
|
|
207
|
-
|
|
208
|
-
If you use TypeScript and React < 16.4 make sure to create a file `contextAPIPatch.d.ts` file with the next content:
|
|
209
|
-
|
|
210
|
-
```ts
|
|
211
|
-
declare module 'react' {
|
|
212
|
-
export type Consumer<T> = any;
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
and include it in your `tscofig.json`, otherwise your app won't compile:
|
|
217
|
-
|
|
218
|
-
```json
|
|
219
|
-
{
|
|
220
|
-
// other configuration options
|
|
221
|
-
"include": [
|
|
222
|
-
"src/**/*",
|
|
223
|
-
"./contextAPIPatch.d.ts" // <-- add this line
|
|
224
|
-
]
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
175
|
### Property names and aliases
|
|
229
176
|
|
|
230
177
|
As you can see from the code above, component name and its property names and values create an English sentence, actually a question. For example, the code below reads as `Can I create a Post`:
|
|
@@ -270,10 +217,10 @@ Majority of applications that need permission checking support have something li
|
|
|
270
217
|
|
|
271
218
|
Let's imagine that server returns user with a role on login:
|
|
272
219
|
|
|
273
|
-
```ts @{data-filename="Login.
|
|
220
|
+
```ts @{data-filename="Login.tsx"}
|
|
274
221
|
import { AbilityBuilder, Ability } from '@casl/ability';
|
|
275
|
-
import React, { useState
|
|
276
|
-
import {
|
|
222
|
+
import React, { useState } from 'react';
|
|
223
|
+
import { useAbility } from '@casl/react';
|
|
277
224
|
|
|
278
225
|
function updateAbility(ability, user) {
|
|
279
226
|
const { can, rules } = new AbilityBuilder(Ability);
|
|
@@ -290,7 +237,7 @@ function updateAbility(ability, user) {
|
|
|
290
237
|
export default () => {
|
|
291
238
|
const [username, setUsername] = useState('');
|
|
292
239
|
const [password, setPassword] = useState('');
|
|
293
|
-
const ability =
|
|
240
|
+
const ability = useAbility();
|
|
294
241
|
const login = () => {
|
|
295
242
|
const params = {
|
|
296
243
|
method: 'POST',
|
|
@@ -314,7 +261,7 @@ export default () => {
|
|
|
314
261
|
|
|
315
262
|
## `useAbility` usage within hooks
|
|
316
263
|
|
|
317
|
-
Using the return value `ability` of `const ability = useAbility(
|
|
264
|
+
Using the return value `ability` of `const ability = useAbility()` within hook dependencies won't trigger a rerender when the rules are updated. You have to specify `ability.rules`:
|
|
318
265
|
|
|
319
266
|
```jsx
|
|
320
267
|
const posts = React.useMemo(() => getPosts(ability), [ability.rules]);
|
|
@@ -335,4 +282,3 @@ If you'd like to help us sustain our community and project, consider [to become
|
|
|
335
282
|
|
|
336
283
|
[contributing]: https://github.com/stalniy/casl/blob/master/CONTRIBUTING.md
|
|
337
284
|
[React]: https://reactjs.org/
|
|
338
|
-
[react-ctx-api]: https://reactjs.org/docs/context.html
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, {
|
|
2
|
+
value: "Module"
|
|
3
|
+
});
|
|
4
|
+
|
|
5
|
+
var e = Object.create, t = Object.defineProperty, r = Object.getOwnPropertyDescriptor, o = Object.getOwnPropertyNames, n = Object.getPrototypeOf, l = Object.prototype.hasOwnProperty;
|
|
6
|
+
|
|
7
|
+
let u = require("react");
|
|
8
|
+
|
|
9
|
+
var c, i, a;
|
|
10
|
+
|
|
11
|
+
i = 1, a = null != (c = u) ? e(n(c)) : {}, u = ((e, n, u, c) => {
|
|
12
|
+
if (n && "object" == typeof n || "function" == typeof n) for (var i, a = o(n), b = 0, s = a.length; b < s; b++) i = a[b],
|
|
13
|
+
l.call(e, i) || i === u || t(e, i, {
|
|
14
|
+
get: (e => n[e]).bind(null, i),
|
|
15
|
+
enumerable: !(c = r(n, i)) || c.enumerable
|
|
16
|
+
});
|
|
17
|
+
return e;
|
|
18
|
+
})(!i && c && c.t ? a : t(a, "default", {
|
|
19
|
+
value: c,
|
|
20
|
+
enumerable: !0
|
|
21
|
+
}), c);
|
|
22
|
+
|
|
23
|
+
const b = (0, u.createContext)(null), s = [], f = () => {};
|
|
24
|
+
|
|
25
|
+
function p() {
|
|
26
|
+
const e = (0, u.useContext)(b), t = (0, u.useCallback)(t => e?.on("updated", t) || f, [ e ]), r = (0,
|
|
27
|
+
u.useCallback)(() => e?.rules || s, [ e ]);
|
|
28
|
+
if ((0, u.useSyncExternalStore)(t, r, r), !e) throw new Error("AbilityContext is not provided. Please make sure to wrap your component tree with <AbilityProvider>.");
|
|
29
|
+
return e;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const d = u.default.memo(function(e) {
|
|
33
|
+
const t = p(), r = e, o = r.of || r.a || r.an || r.this || r.on, n = r.I || r.do, l = r.field, c = u.default.useMemo(() => t.relevantRuleFor(n, o, l), [ t, t.rules, n, o, l ]);
|
|
34
|
+
let i = !!c && !c.inverted;
|
|
35
|
+
e.not && (i = !i);
|
|
36
|
+
const a = "function" == typeof e.children ? e.children({
|
|
37
|
+
isAllowed: i,
|
|
38
|
+
ability: t,
|
|
39
|
+
reason: c?.reason
|
|
40
|
+
}) : e.children;
|
|
41
|
+
return e.passThrough || i ? a : null;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
exports.AbilityProvider = function({children: e, value: t}) {
|
|
45
|
+
return (0, u.createElement)(b.Provider, {
|
|
46
|
+
value: t
|
|
47
|
+
}, e);
|
|
48
|
+
}, exports.Can = d, exports.useAbility = p;
|
|
49
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["React"],"sources":["../../src/hooks/useAbility.ts","../../src/Can.ts"],"sourcesContent":["import { createElement, useContext, useCallback, useSyncExternalStore, createContext } from 'react';\nimport type { AnyAbility } from '@casl/ability';\n\nconst AbilityContext = createContext<AnyAbility | null>(null);\nconst EMPTY_RULES: any[] = [];\nconst noop = () => {};\n\nexport function AbilityProvider<T extends AnyAbility>({\n children,\n value,\n}: {\n children: React.ReactNode;\n value: T;\n}) {\n return createElement(AbilityContext.Provider, { value }, children);\n}\n\nexport function useAbility<T extends AnyAbility>(): T {\n const ability = useContext(AbilityContext);\n const subscribe = useCallback(\n (callback: () => void) => ability?.on('updated', callback) || noop,\n [ability],\n );\n const getSnapshot = useCallback(() => ability?.rules || EMPTY_RULES, [ability]);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n if (!ability) {\n throw new Error('AbilityContext is not provided. Please make sure to wrap your component tree with <AbilityProvider>.');\n }\n\n return ability as T;\n}\n","import React, { ReactNode } from 'react';\nimport {\n AbilityTuple,\n SubjectType,\n AnyAbility,\n Generics,\n Abilities,\n IfString,\n} from '@casl/ability';\nimport { useAbility } from './hooks/useAbility';\n\ntype AbilityCanProps<\n T extends Abilities,\n Else = IfString<T, { do: T } | { I: T }>\n> = T extends AbilityTuple\n ? { do: T[0], on: T[1], field?: string } |\n { I: T[0], a: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], an: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], this: Exclude<T[1], SubjectType>, field?: string }\n : Else;\n\ninterface ExtraProps {\n not?: boolean\n passThrough?: boolean\n}\n\ninterface CanExtraProps<T extends AnyAbility> extends ExtraProps {\n children: ReactNode | ((exposes: {\n isAllowed: boolean;\n ability: T;\n reason: string | undefined;\n }) => ReactNode)\n}\n\nexport type CanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & CanExtraProps<T>;\n\nfunction CanComponent<T extends AnyAbility>(props: CanProps<T>) {\n const ability = useAbility();\n const propsWithAliases = props as CanProps<T> & {\n of?: unknown\n a?: unknown\n an?: unknown\n this?: unknown\n on?: unknown\n I?: unknown\n do?: unknown\n field?: string\n };\n const subject =\n propsWithAliases.of ||\n propsWithAliases.a ||\n propsWithAliases.an ||\n propsWithAliases.this ||\n propsWithAliases.on;\n const action = propsWithAliases.I || propsWithAliases.do;\n const field = propsWithAliases.field;\n const rule = React.useMemo(() => {\n return ability.relevantRuleFor(action, subject, field);\n }, [ability, ability.rules, action, subject, field]);\n let isAllowed = !!rule && !rule.inverted;\n if (props.not) isAllowed = !isAllowed;\n\n const elements = typeof props.children === 'function'\n ? props.children({ isAllowed, ability: ability as T, reason: rule?.reason })\n : props.children;\n\n return props.passThrough || isAllowed ? elements as ReactNode : null;\n}\n\nexport const Can = React.memo(CanComponent) as typeof CanComponent;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAGA,MAAM,KAAA,GAAA,EAAA,eAAkD,OAClD,IAAqB,IACrB,IAAA;;AAYN,SAAgB;IACd,MAAM,KAAA,GAAA,EAAA,YAAqB,IACrB,KAAA,GAAA,EAAA,aACH,KAAyB,GAAS,GAAG,WAAW,MAAa,GAC9D,EAAC,MAEG,KAAA;IAAA,EAAA,aAAA,MAAgC,GAAS,SAAS,GAAa,EAAC;IAItE,KAFA,GAAA,EAAA,sBAAqB,GAAW,GAAa,KAExC,GACH,MAAM,IAAI,MAAM;IAGlB,OAAO;;;ACuCT,MAAa,IAAMA,EAAAA,QAAM,KAjCzB,SAA4C;IAC1C,MAAM,IAAU,KACV,IAAmB,GAUnB,IACJ,EAAiB,MACjB,EAAiB,KACjB,EAAiB,MACjB,EAAiB,QACjB,EAAiB,IACb,IAAS,EAAiB,KAAK,EAAiB,IAChD,IAAQ,EAAiB,OACzB,IAAOA,EAAAA,QAAM,QAAA,MACV,EAAQ,gBAAgB,GAAQ,GAAS,IAC/C,EAAC,GAAS,EAAQ,OAAO,GAAQ,GAAS;IAC7C,IAAI,MAAc,MAAS,EAAK;IAC5B,EAAM,QAAK,KAAa;IAE5B,MAAM,IAAqC,qBAAnB,EAAM,WAC1B,EAAM,SAAS;QAAE;QAAoB;QAAc,QAAQ,GAAM;SACjE,EAAM;IAEV,OAAO,EAAM,eAAe,IAAY,IAAwB;;;0BD5DlE,UAAsD,UACpD,GAAA,OACA;IAKA,QAAA,GAAA,EAAA,eAAqB,EAAe,UAAU;QAAE;OAAS"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import t, { createContext as n, createElement as o, useCallback as e, useContext as r, useSyncExternalStore as i } from "react";
|
|
2
|
+
|
|
3
|
+
const l = n(null), u = [], c = () => {};
|
|
4
|
+
|
|
5
|
+
function s({children: t, value: n}) {
|
|
6
|
+
return o(l.Provider, {
|
|
7
|
+
value: n
|
|
8
|
+
}, t);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function a() {
|
|
12
|
+
const t = r(l), n = e(n => t?.on("updated", n) || c, [ t ]), o = e(() => t?.rules || u, [ t ]);
|
|
13
|
+
if (i(n, o, o), !t) throw new Error("AbilityContext is not provided. Please make sure to wrap your component tree with <AbilityProvider>.");
|
|
14
|
+
return t;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const d = t.memo(function(n) {
|
|
18
|
+
const o = a(), e = n, r = e.of || e.a || e.an || e.this || e.on, i = e.I || e.do, l = e.field, u = t.useMemo(() => o.relevantRuleFor(i, r, l), [ o, o.rules, i, r, l ]);
|
|
19
|
+
let c = !!u && !u.inverted;
|
|
20
|
+
n.not && (c = !c);
|
|
21
|
+
const s = "function" == typeof n.children ? n.children({
|
|
22
|
+
isAllowed: c,
|
|
23
|
+
ability: o,
|
|
24
|
+
reason: u?.reason
|
|
25
|
+
}) : n.children;
|
|
26
|
+
return n.passThrough || c ? s : null;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export { s as AbilityProvider, d as Can, a as useAbility };
|
|
30
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/hooks/useAbility.ts","../../src/Can.ts"],"sourcesContent":["import { createElement, useContext, useCallback, useSyncExternalStore, createContext } from 'react';\nimport type { AnyAbility } from '@casl/ability';\n\nconst AbilityContext = createContext<AnyAbility | null>(null);\nconst EMPTY_RULES: any[] = [];\nconst noop = () => {};\n\nexport function AbilityProvider<T extends AnyAbility>({\n children,\n value,\n}: {\n children: React.ReactNode;\n value: T;\n}) {\n return createElement(AbilityContext.Provider, { value }, children);\n}\n\nexport function useAbility<T extends AnyAbility>(): T {\n const ability = useContext(AbilityContext);\n const subscribe = useCallback(\n (callback: () => void) => ability?.on('updated', callback) || noop,\n [ability],\n );\n const getSnapshot = useCallback(() => ability?.rules || EMPTY_RULES, [ability]);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n if (!ability) {\n throw new Error('AbilityContext is not provided. Please make sure to wrap your component tree with <AbilityProvider>.');\n }\n\n return ability as T;\n}\n","import React, { ReactNode } from 'react';\nimport {\n AbilityTuple,\n SubjectType,\n AnyAbility,\n Generics,\n Abilities,\n IfString,\n} from '@casl/ability';\nimport { useAbility } from './hooks/useAbility';\n\ntype AbilityCanProps<\n T extends Abilities,\n Else = IfString<T, { do: T } | { I: T }>\n> = T extends AbilityTuple\n ? { do: T[0], on: T[1], field?: string } |\n { I: T[0], a: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], an: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], this: Exclude<T[1], SubjectType>, field?: string }\n : Else;\n\ninterface ExtraProps {\n not?: boolean\n passThrough?: boolean\n}\n\ninterface CanExtraProps<T extends AnyAbility> extends ExtraProps {\n children: ReactNode | ((exposes: {\n isAllowed: boolean;\n ability: T;\n reason: string | undefined;\n }) => ReactNode)\n}\n\nexport type CanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & CanExtraProps<T>;\n\nfunction CanComponent<T extends AnyAbility>(props: CanProps<T>) {\n const ability = useAbility();\n const propsWithAliases = props as CanProps<T> & {\n of?: unknown\n a?: unknown\n an?: unknown\n this?: unknown\n on?: unknown\n I?: unknown\n do?: unknown\n field?: string\n };\n const subject =\n propsWithAliases.of ||\n propsWithAliases.a ||\n propsWithAliases.an ||\n propsWithAliases.this ||\n propsWithAliases.on;\n const action = propsWithAliases.I || propsWithAliases.do;\n const field = propsWithAliases.field;\n const rule = React.useMemo(() => {\n return ability.relevantRuleFor(action, subject, field);\n }, [ability, ability.rules, action, subject, field]);\n let isAllowed = !!rule && !rule.inverted;\n if (props.not) isAllowed = !isAllowed;\n\n const elements = typeof props.children === 'function'\n ? props.children({ isAllowed, ability: ability as T, reason: rule?.reason })\n : props.children;\n\n return props.passThrough || isAllowed ? elements as ReactNode : null;\n}\n\nexport const Can = React.memo(CanComponent) as typeof CanComponent;\n"],"mappings":";;AAGA,MAAM,IAAiB,EAAiC,OAClD,IAAqB,IACrB,IAAA;;AAEN,SAAgB,GAAsC,UACpD,GAAA,OACA;IAKA,OAAO,EAAc,EAAe,UAAU;QAAE;OAAS;;;AAG3D,SAAgB;IACd,MAAM,IAAU,EAAW,IACrB,IAAY,EACf,KAAyB,GAAS,GAAG,WAAW,MAAa,GAC9D,EAAC,MAEG,IAAc,EAAA,MAAkB,GAAS,SAAS,GAAa,EAAC;IAItE,IAFA,EAAqB,GAAW,GAAa,KAExC,GACH,MAAM,IAAI,MAAM;IAGlB,OAAO;;;ACuCT,MAAa,IAAM,EAAM,KAjCzB,SAA4C;IAC1C,MAAM,IAAU,KACV,IAAmB,GAUnB,IACJ,EAAiB,MACjB,EAAiB,KACjB,EAAiB,MACjB,EAAiB,QACjB,EAAiB,IACb,IAAS,EAAiB,KAAK,EAAiB,IAChD,IAAQ,EAAiB,OACzB,IAAO,EAAM,QAAA,MACV,EAAQ,gBAAgB,GAAQ,GAAS,IAC/C,EAAC,GAAS,EAAQ,OAAO,GAAQ,GAAS;IAC7C,IAAI,MAAc,MAAS,EAAK;IAC5B,EAAM,QAAK,KAAa;IAE5B,MAAM,IAAqC,qBAAnB,EAAM,WAC1B,EAAM,SAAS;QAAE;QAAoB;QAAc,QAAQ,GAAM;SACjE,EAAM;IAEV,OAAO,EAAM,eAAe,IAAY,IAAwB"}
|
package/dist/types/Can.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
2
|
import { AbilityTuple, SubjectType, AnyAbility, Generics, Abilities, IfString } from '@casl/ability';
|
|
3
3
|
type AbilityCanProps<T extends Abilities, Else = IfString<T, {
|
|
4
4
|
do: T;
|
|
@@ -26,29 +26,13 @@ interface ExtraProps {
|
|
|
26
26
|
passThrough?: boolean;
|
|
27
27
|
}
|
|
28
28
|
interface CanExtraProps<T extends AnyAbility> extends ExtraProps {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode);
|
|
29
|
+
children: ReactNode | ((exposes: {
|
|
30
|
+
isAllowed: boolean;
|
|
31
|
+
ability: T;
|
|
32
|
+
reason: string | undefined;
|
|
33
|
+
}) => ReactNode);
|
|
35
34
|
}
|
|
36
35
|
export type CanProps<T extends AnyAbility> = AbilityCanProps<Generics<T>['abilities']> & CanExtraProps<T>;
|
|
37
|
-
|
|
38
|
-
export declare
|
|
39
|
-
t: boolean;
|
|
40
|
-
}> {
|
|
41
|
-
private _isAllowed;
|
|
42
|
-
private _ability;
|
|
43
|
-
private _unsubscribeFromAbility;
|
|
44
|
-
state: {
|
|
45
|
-
t: boolean;
|
|
46
|
-
};
|
|
47
|
-
componentWillUnmount(): void;
|
|
48
|
-
private _connectToAbility;
|
|
49
|
-
get allowed(): boolean;
|
|
50
|
-
private _canRender;
|
|
51
|
-
render(): ReactNode;
|
|
52
|
-
private _renderChildren;
|
|
53
|
-
}
|
|
36
|
+
declare function CanComponent<T extends AnyAbility>(props: CanProps<T>): React.ReactNode;
|
|
37
|
+
export declare const Can: typeof CanComponent;
|
|
54
38
|
export {};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { AnyAbility } from '@casl/ability';
|
|
2
|
+
export declare function AbilityProvider<T extends AnyAbility>({ children, value, }: {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
value: T;
|
|
5
|
+
}): import("react").FunctionComponentElement<import("react").ProviderProps<AnyAbility | null>>;
|
|
6
|
+
export declare function useAbility<T extends AnyAbility>(): T;
|
package/dist/types/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casl/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-rc",
|
|
4
4
|
"description": "React component for CASL which makes it easy to add permissions in any React application",
|
|
5
|
-
"main": "dist/
|
|
6
|
-
"
|
|
7
|
-
"es2015": "dist/es6m/index.mjs",
|
|
8
|
-
"typings": "./index.d.ts",
|
|
5
|
+
"main": "./dist/esm/index.mjs",
|
|
6
|
+
"type": "module",
|
|
9
7
|
"exports": {
|
|
10
8
|
".": {
|
|
11
9
|
"types": "./dist/types/index.d.ts",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
10
|
+
"require": "./dist/cjs/index.cjs",
|
|
11
|
+
"import": "./dist/esm/index.mjs"
|
|
14
12
|
}
|
|
15
13
|
},
|
|
16
14
|
"repository": {
|
|
@@ -32,11 +30,10 @@
|
|
|
32
30
|
"author": "Sergii Stotskyi <sergiy.stotskiy@gmail.com>",
|
|
33
31
|
"license": "MIT",
|
|
34
32
|
"peerDependencies": {
|
|
35
|
-
"@casl/ability": "^4.0.0 || ^5.1.0 || ^6.0.0",
|
|
33
|
+
"@casl/ability": "^4.0.0 || ^5.1.0 || ^6.0.0 || ^7.0.0",
|
|
36
34
|
"react": "^18.0.0 || ^19.0.0"
|
|
37
35
|
},
|
|
38
36
|
"devDependencies": {
|
|
39
|
-
"@casl/ability": "^6.0.0",
|
|
40
37
|
"@casl/dx": "^1.0.0",
|
|
41
38
|
"@testing-library/dom": "^10.4.0",
|
|
42
39
|
"@testing-library/react": "^16.1.0",
|
|
@@ -46,19 +43,18 @@
|
|
|
46
43
|
"chai": "^4.1.0",
|
|
47
44
|
"chai-spies": "^1.0.0",
|
|
48
45
|
"react": "^19.0.0",
|
|
49
|
-
"react-dom": "^19.0.0"
|
|
46
|
+
"react-dom": "^19.0.0",
|
|
47
|
+
"@casl/ability": "7.0.0-rc"
|
|
50
48
|
},
|
|
51
49
|
"files": [
|
|
52
|
-
"dist"
|
|
53
|
-
"*.d.ts"
|
|
50
|
+
"dist"
|
|
54
51
|
],
|
|
55
52
|
"scripts": {
|
|
56
|
-
"build.prepare": "rm -rf dist/* &&
|
|
57
|
-
"build": "npm run build.prepare &&
|
|
58
|
-
"build.types": "dx tsc",
|
|
53
|
+
"build.prepare": "rm -rf dist/* && dx tsc",
|
|
54
|
+
"build": "npm run build.prepare && dx tsdown",
|
|
59
55
|
"lint": "dx eslint src/ spec/",
|
|
60
56
|
"test": "dx jest --env jsdom",
|
|
61
57
|
"release.prepare": "npm run lint && npm test && NODE_ENV=production npm run build",
|
|
62
|
-
"release": "npm run release.prepare && pnpm publish"
|
|
58
|
+
"release": "npm run release.prepare && pnpm publish --tag \"$RELEASE_DIST_TAG\" --publish-branch \"$RELEASE_PUBLISH_BRANCH\""
|
|
63
59
|
}
|
|
64
60
|
}
|
package/dist/es5m/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import t,{PureComponent as r,createElement as n}from"react";function i(t,r){for(var n=0;n<r.length;n++){var i=r[n];i.enumerable=i.enumerable||false,i.configurable=true,"value"in i&&(i.writable=true),Object.defineProperty(t,a(i.key),i)}}function u(t,r,n){return r&&i(t.prototype,r),Object.defineProperty(t,"prototype",{writable:false}),t}function e(){return e=Object.assign?Object.assign.bind():function(t){for(var r=1;r<arguments.length;r++){var n=arguments[r];for(var i in n)({}).hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t},e.apply(null,arguments)}function o(t,r){t.prototype=Object.create(r.prototype),t.prototype.constructor=t,f(t,r)}function f(t,r){return f=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,r){return t.__proto__=r,t},f(t,r)}function c(t,r){if("object"!=typeof t||!t)return t;var n=t[Symbol.toPrimitive];if(void 0!==n){var i=n.call(t,r);if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}function a(t){var r=c(t,"string");return"symbol"==typeof r?r:r+""}var s=function t(){};var v=function(t){function r(){var r;for(var n=arguments.length,i=new Array(n),u=0;u<n;u++)i[u]=arguments[u];r=t.call.apply(t,[this].concat(i))||this;r.i=false;r.u=null;r.o=s;r.state={t:true};return r}o(r,t);var n=r.prototype;n.componentWillUnmount=function t(){this.o()};n.v=function _connectToAbility(t){var r=this;if(t===this.u)return;this.o();this.u=null;if(t){this.u=t;this.o=t.on("updated",function(){return r.setState({t:!r.state.t})})}};n.l=function t(){var r=this.props;var n=r.of||r.a||r.an||r.this||r.on;var i=r.not?"cannot":"can";return r.ability[i](r.I||r.do,n,r.field)};n.render=function t(){this.v(this.props.ability);this.i=this.l();return this.props.passThrough||this.i?this.h():null};n.h=function t(){var r=this.props,n=r.children,i=r.ability;var u=typeof n==="function"?n(this.i,i):n;return u};return u(r,[{key:"allowed",get:function t(){return this.i}}])}(r);function l(t){return function(r){return n(t,{children:function t(i){return n(v,e({},r,{ability:r.ability||i}))}})}}function useAbility(r){var n=t.useContext(r);var i=t.useCallback(function(t){return n.on("updated",t)},[n]);var u=t.useCallback(function(){return n.rules},[n]);t.useSyncExternalStore(i,u,u);return n}export{v as Can,l as createContextualCan,useAbility};
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
package/dist/es5m/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/Can.ts","../../src/factory.ts","../../src/hooks/useAbility.ts"],"sourcesContent":["import { PureComponent, ReactNode } from 'react';\nimport {\n Unsubscribe,\n AbilityTuple,\n SubjectType,\n AnyAbility,\n Generics,\n Abilities,\n IfString,\n} from '@casl/ability';\n\nconst noop = () => {};\n\ntype AbilityCanProps<\n T extends Abilities,\n Else = IfString<T, { do: T } | { I: T }>\n> = T extends AbilityTuple\n ? { do: T[0], on: T[1], field?: string } |\n { I: T[0], a: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], an: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], this: Exclude<T[1], SubjectType>, field?: string }\n : Else;\n\ninterface ExtraProps {\n not?: boolean\n passThrough?: boolean\n}\n\ninterface CanExtraProps<T extends AnyAbility> extends ExtraProps {\n ability: T\n children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode)\n}\n\ninterface BoundCanExtraProps<T extends AnyAbility> extends ExtraProps {\n ability?: T\n children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode)\n}\n\nexport type CanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & CanExtraProps<T>;\nexport type BoundCanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & BoundCanExtraProps<T>;\n\nexport class Can<T extends AnyAbility> extends PureComponent<CanProps<T>, { t: boolean }> {\n private _isAllowed = false;\n private _ability: T | null = null;\n private _unsubscribeFromAbility: Unsubscribe = noop;\n state = { t: true }\n\n componentWillUnmount() {\n this._unsubscribeFromAbility();\n }\n\n private _connectToAbility(ability?: T) {\n if (ability === this._ability) {\n return;\n }\n\n this._unsubscribeFromAbility();\n this._ability = null;\n\n if (ability) {\n this._ability = ability;\n this._unsubscribeFromAbility = ability.on('updated', () => this.setState({ t: !this.state.t }));\n }\n }\n\n get allowed() {\n return this._isAllowed;\n }\n\n private _canRender(): boolean {\n const props: any = this.props;\n const subject = props.of || props.a || props.an || props.this || props.on;\n const can = props.not ? 'cannot' : 'can';\n\n return props.ability[can](props.I || props.do, subject, props.field);\n }\n\n render() {\n this._connectToAbility(this.props.ability);\n this._isAllowed = this._canRender();\n return this.props.passThrough || this._isAllowed ? this._renderChildren() : null;\n }\n\n private _renderChildren() {\n const { children, ability } = this.props;\n const elements = typeof children === 'function'\n ? children(this._isAllowed, ability as any)\n : children;\n\n return elements as ReactNode;\n }\n}\n","import { AnyAbility } from '@casl/ability';\nimport { Consumer, FunctionComponent, createElement } from 'react';\nimport { BoundCanProps, Can } from './Can';\n\nexport function createContextualCan<T extends AnyAbility>(\n Getter: Consumer<T>\n): FunctionComponent<BoundCanProps<T>> {\n return (props: BoundCanProps<T>) => createElement(Getter, {\n children: (ability: T) => \n createElement(Can, { ...props, ability: props.ability || ability } as any),\n });\n}\n","import React from 'react';\nimport { AnyAbility } from '@casl/ability';\n\nexport function useAbility<T extends AnyAbility>(context: React.Context<T>): T {\n const ability = React.useContext<T>(context);\n\n const subscribe = React.useCallback(\n (callback: () => void) => ability.on('updated', callback),\n [ability],\n );\n\n const getSnapshot = React.useCallback(() => ability.rules, [ability]);\n\n React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n return ability;\n}\n"],"names":["noop","Can","_PureComponent","_this","_len","arguments","length","args","Array","_key","call","apply","this","concat","_isAllowed","_ability","_unsubscribeFromAbility","state","t","_inheritsLoose","_proto","prototype","componentWillUnmount","_connectToAbility","ability","_this2","on","setState","_canRender","props","subject","of","a","an","can","not","I","do","field","render","passThrough","_renderChildren","_this$props","children","elements","_createClass","key","get","PureComponent","createContextualCan","Getter","createElement","_extends","useAbility","context","React","useContext","subscribe","useCallback","callback","getSnapshot","rules","useSyncExternalStore"],"mappings":"giCAWA,IAAMA,EAAO,SAAPA,IAAc,EAgCpB,IAAaC,WAAGC,GAAA,SAAAD,IAAA,IAAAE,EAAA,IAAA,IAAAC,EAAAC,UAAAC,OAAAC,EAAA,IAAAC,MAAAJ,GAAAK,EAAA,EAAAA,EAAAL,EAAAK,IAAAF,EAAAE,GAAAJ,UAAAI,GAAAN,EAAAD,EAAAQ,KAAAC,MAAAT,EAAA,CAAAU,MAAAC,OAAAN,KAAAK,KAAAT,EACNW,EAAa,MAAKX,EAClBY,EAAqB,KAAIZ,EACzBa,EAAuChB,EAAIG,EACnDc,MAAQ,CAAEC,EAAG,MAAM,OAAAf,CAAA,CAAAgB,EAAAlB,EAAAC,GAAA,IAAAkB,EAAAnB,EAAAoB,UAAAD,EAEnBE,qBAAA,SAAAA,IACEV,KAAKI,GACP,EAACI,EAEOG,EAAR,SAAQA,kBAAkBC,GAAa,IAAAC,EAAAb,KACrC,GAAIY,IAAYZ,KAAKG,EACnB,OAGFH,KAAKI,IACLJ,KAAKG,EAAW,KAEhB,GAAIS,EAAS,CACXZ,KAAKG,EAAWS,EAChBZ,KAAKI,EAA0BQ,EAAQE,GAAG,UAAW,WAAA,OAAMD,EAAKE,SAAS,CAAET,GAAIO,EAAKR,MAAMC,GAAI,EAChG,CACF,EAACE,EAMOQ,EAAR,SAAQA,IACN,IAAMC,EAAajB,KAAKiB,MACxB,IAAMC,EAAUD,EAAME,IAAMF,EAAMG,GAAKH,EAAMI,IAAMJ,EAAMjB,MAAQiB,EAAMH,GACvE,IAAMQ,EAAML,EAAMM,IAAM,SAAW,MAEnC,OAAON,EAAML,QAAQU,GAAKL,EAAMO,GAAKP,EAAMQ,GAAIP,EAASD,EAAMS,MAChE,EAAClB,EAEDmB,OAAA,SAAAA,IACE3B,KAAKW,EAAkBX,KAAKiB,MAAML,SAClCZ,KAAKE,EAAaF,KAAKgB,IACvB,OAAOhB,KAAKiB,MAAMW,aAAe5B,KAAKE,EAAaF,KAAK6B,IAAoB,IAC9E,EAACrB,EAEOqB,EAAR,SAAQA,IACN,IAAAC,EAA8B9B,KAAKiB,MAA3Bc,EAAQD,EAARC,SAAUnB,EAAOkB,EAAPlB,QAClB,IAAMoB,SAAkBD,IAAa,WACjCA,EAAS/B,KAAKE,EAAYU,GAC1BmB,EAEJ,OAAOC,CACT,EAAC,OAAAC,EAAA5C,EAAA,CAAA,CAAA6C,IAAA,UAAAC,IAzBD,SAAAA,IACE,OAAOnC,KAAKE,CACd,IAAC,EA1B4CkC,GCvCxC,SAASC,EACdC,GAEA,OAAO,SAACrB,GAAuB,OAAKsB,EAAcD,EAAQ,CACxDP,SAAU,SAAVA,EAAWnB,GAAU,OACnB2B,EAAclD,EAAGmD,KAAOvB,EAAK,CAAEL,QAASK,EAAML,SAAWA,IAAiB,GAC5E,CACJ,CCRO,SAAS6B,WAAiCC,GAC/C,IAAM9B,EAAU+B,EAAMC,WAAcF,GAEpC,IAAMG,EAAYF,EAAMG,YACtB,SAACC,GAAoB,OAAKnC,EAAQE,GAAG,UAAWiC,EAAS,EACzD,CAACnC,IAGH,IAAMoC,EAAcL,EAAMG,YAAY,WAAA,OAAMlC,EAAQqC,KAAK,EAAE,CAACrC,IAE5D+B,EAAMO,qBAAqBL,EAAWG,EAAaA,GAEnD,OAAOpC,CACT"}
|
package/dist/es6m/index.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import t,{PureComponent as i,createElement as n}from"react";const s=()=>{};class e extends i{constructor(...t){super(...t);this.i=false;this.h=null;this.o=s;this.state={t:true}}componentWillUnmount(){this.o()}u(t){if(t===this.h)return;this.o();this.h=null;if(t){this.h=t;this.o=t.on("updated",()=>this.setState({t:!this.state.t}))}}get allowed(){return this.i}l(){const t=this.props;const i=t.of||t.a||t.an||t.this||t.on;const n=t.not?"cannot":"can";return t.ability[n](t.I||t.do,i,t.field)}render(){this.u(this.props.ability);this.i=this.l();return this.props.passThrough||this.i?this.p():null}p(){const{children:t,ability:i}=this.props;const n=typeof t==="function"?t(this.i,i):t;return n}}function r(t){return i=>n(t,{children:t=>n(e,Object.assign({},i,{ability:i.ability||t}))})}function useAbility(i){const n=t.useContext(i);const s=t.useCallback(t=>n.on("updated",t),[n]);const e=t.useCallback(()=>n.rules,[n]);t.useSyncExternalStore(s,e,e);return n}export{e as Can,r as createContextualCan,useAbility};
|
|
2
|
-
//# sourceMappingURL=index.mjs.map
|
package/dist/es6m/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/Can.ts","../../src/factory.ts","../../src/hooks/useAbility.ts"],"sourcesContent":["import { PureComponent, ReactNode } from 'react';\nimport {\n Unsubscribe,\n AbilityTuple,\n SubjectType,\n AnyAbility,\n Generics,\n Abilities,\n IfString,\n} from '@casl/ability';\n\nconst noop = () => {};\n\ntype AbilityCanProps<\n T extends Abilities,\n Else = IfString<T, { do: T } | { I: T }>\n> = T extends AbilityTuple\n ? { do: T[0], on: T[1], field?: string } |\n { I: T[0], a: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], an: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], this: Exclude<T[1], SubjectType>, field?: string }\n : Else;\n\ninterface ExtraProps {\n not?: boolean\n passThrough?: boolean\n}\n\ninterface CanExtraProps<T extends AnyAbility> extends ExtraProps {\n ability: T\n children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode)\n}\n\ninterface BoundCanExtraProps<T extends AnyAbility> extends ExtraProps {\n ability?: T\n children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode)\n}\n\nexport type CanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & CanExtraProps<T>;\nexport type BoundCanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & BoundCanExtraProps<T>;\n\nexport class Can<T extends AnyAbility> extends PureComponent<CanProps<T>, { t: boolean }> {\n private _isAllowed = false;\n private _ability: T | null = null;\n private _unsubscribeFromAbility: Unsubscribe = noop;\n state = { t: true }\n\n componentWillUnmount() {\n this._unsubscribeFromAbility();\n }\n\n private _connectToAbility(ability?: T) {\n if (ability === this._ability) {\n return;\n }\n\n this._unsubscribeFromAbility();\n this._ability = null;\n\n if (ability) {\n this._ability = ability;\n this._unsubscribeFromAbility = ability.on('updated', () => this.setState({ t: !this.state.t }));\n }\n }\n\n get allowed() {\n return this._isAllowed;\n }\n\n private _canRender(): boolean {\n const props: any = this.props;\n const subject = props.of || props.a || props.an || props.this || props.on;\n const can = props.not ? 'cannot' : 'can';\n\n return props.ability[can](props.I || props.do, subject, props.field);\n }\n\n render() {\n this._connectToAbility(this.props.ability);\n this._isAllowed = this._canRender();\n return this.props.passThrough || this._isAllowed ? this._renderChildren() : null;\n }\n\n private _renderChildren() {\n const { children, ability } = this.props;\n const elements = typeof children === 'function'\n ? children(this._isAllowed, ability as any)\n : children;\n\n return elements as ReactNode;\n }\n}\n","import { AnyAbility } from '@casl/ability';\nimport { Consumer, FunctionComponent, createElement } from 'react';\nimport { BoundCanProps, Can } from './Can';\n\nexport function createContextualCan<T extends AnyAbility>(\n Getter: Consumer<T>\n): FunctionComponent<BoundCanProps<T>> {\n return (props: BoundCanProps<T>) => createElement(Getter, {\n children: (ability: T) => \n createElement(Can, { ...props, ability: props.ability || ability } as any),\n });\n}\n","import React from 'react';\nimport { AnyAbility } from '@casl/ability';\n\nexport function useAbility<T extends AnyAbility>(context: React.Context<T>): T {\n const ability = React.useContext<T>(context);\n\n const subscribe = React.useCallback(\n (callback: () => void) => ability.on('updated', callback),\n [ability],\n );\n\n const getSnapshot = React.useCallback(() => ability.rules, [ability]);\n\n React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n return ability;\n}\n"],"names":["noop","Can","PureComponent","constructor","args","super","this","_isAllowed","_ability","_unsubscribeFromAbility","state","t","componentWillUnmount","_connectToAbility","ability","on","setState","allowed","_canRender","props","subject","of","a","an","can","not","I","do","field","render","passThrough","_renderChildren","children","elements","createContextualCan","Getter","createElement","Object","assign","useAbility","context","React","useContext","subscribe","useCallback","callback","getSnapshot","rules","useSyncExternalStore"],"mappings":"4DAWA,MAAMA,EAAOA,OAgCN,MAAMC,UAAkCC,EAA2CC,WAAAA,IAAAC,GAAAC,SAAAD,GAAAE,KAChFC,EAAa,MAAKD,KAClBE,EAAqB,KAAIF,KACzBG,EAAuCT,EAAIM,KACnDI,MAAQ,CAAEC,EAAG,KAAM,CAEnBC,oBAAAA,GACEN,KAAKG,GACP,CAEQI,CAAAA,CAAkBC,GACxB,GAAIA,IAAYR,KAAKE,EACnB,OAGFF,KAAKG,IACLH,KAAKE,EAAW,KAEhB,GAAIM,EAAS,CACXR,KAAKE,EAAWM,EAChBR,KAAKG,EAA0BK,EAAQC,GAAG,UAAW,IAAMT,KAAKU,SAAS,CAAEL,GAAIL,KAAKI,MAAMC,IAC5F,CACF,CAEA,WAAIM,GACF,OAAOX,KAAKC,CACd,CAEQW,CAAAA,GACN,MAAMC,EAAab,KAAKa,MACxB,MAAMC,EAAUD,EAAME,IAAMF,EAAMG,GAAKH,EAAMI,IAAMJ,EAAMb,MAAQa,EAAMJ,GACvE,MAAMS,EAAML,EAAMM,IAAM,SAAW,MAEnC,OAAON,EAAML,QAAQU,GAAKL,EAAMO,GAAKP,EAAMQ,GAAIP,EAASD,EAAMS,MAChE,CAEAC,MAAAA,GACEvB,KAAKO,EAAkBP,KAAKa,MAAML,SAClCR,KAAKC,EAAaD,KAAKY,IACvB,OAAOZ,KAAKa,MAAMW,aAAexB,KAAKC,EAAaD,KAAKyB,IAAoB,IAC9E,CAEQA,CAAAA,GACN,MAAMC,SAAEA,EAAQlB,QAAEA,GAAYR,KAAKa,MACnC,MAAMc,SAAkBD,IAAa,WACjCA,EAAS1B,KAAKC,EAAYO,GAC1BkB,EAEJ,OAAOC,CACT,ECxFK,SAASC,EACdC,GAEA,OAAQhB,GAA4BiB,EAAcD,EAAQ,CACxDH,SAAWlB,GACTsB,EAAcnC,EAAGoC,OAAAC,OAAA,CAAA,EAAOnB,EAAK,CAAEL,QAASK,EAAML,SAAWA,MAE/D,CCRO,SAASyB,WAAiCC,GAC/C,MAAM1B,EAAU2B,EAAMC,WAAcF,GAEpC,MAAMG,EAAYF,EAAMG,YACrBC,GAAyB/B,EAAQC,GAAG,UAAW8B,GAChD,CAAC/B,IAGH,MAAMgC,EAAcL,EAAMG,YAAY,IAAM9B,EAAQiC,MAAO,CAACjC,IAE5D2B,EAAMO,qBAAqBL,EAAWG,EAAaA,GAEnD,OAAOhC,CACT"}
|
package/dist/types/factory.d.ts
DELETED
package/dist/umd/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
(function(t,n){typeof exports==="object"&&typeof module!=="undefined"?n(exports,require("react")):typeof define==="function"&&define.amd?define(["exports","react"],n):(t=typeof globalThis!=="undefined"?globalThis:t||self,n((t.casl=t.casl||{},t.casl.react={}),t.React))})(this,function(t,n){"use strict";function r(t,n){for(var r=0;r<n.length;r++){var e=n[r];e.enumerable=e.enumerable||false,e.configurable=true,"value"in e&&(e.writable=true),Object.defineProperty(t,c(e.key),e)}}function e(t,n,e){return n&&r(t.prototype,n),Object.defineProperty(t,"prototype",{writable:false}),t}function i(){return i=Object.assign?Object.assign.bind():function(t){for(var n=1;n<arguments.length;n++){var r=arguments[n];for(var e in r)({}).hasOwnProperty.call(r,e)&&(t[e]=r[e])}return t},i.apply(null,arguments)}function u(t,n){t.prototype=Object.create(n.prototype),t.prototype.constructor=t,o(t,n)}function o(t,n){return o=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,n){return t.__proto__=n,t},o(t,n)}function f(t,n){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var e=r.call(t,n);if("object"!=typeof e)return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}function c(t){var n=f(t,"string");return"symbol"==typeof n?n:n+""}var a=function t(){};var s=function(t){function n(){var n;for(var r=arguments.length,e=new Array(r),i=0;i<r;i++)e[i]=arguments[i];n=t.call.apply(t,[this].concat(e))||this;n.i=false;n.u=null;n.o=a;n.state={t:true};return n}u(n,t);var r=n.prototype;r.componentWillUnmount=function t(){this.o()};r.l=function _connectToAbility(t){var n=this;if(t===this.u)return;this.o();this.u=null;if(t){this.u=t;this.o=t.on("updated",function(){return n.setState({t:!n.state.t})})}};r.v=function t(){var n=this.props;var r=n.of||n.a||n.an||n.this||n.on;var e=n.not?"cannot":"can";return n.ability[e](n.I||n.do,r,n.field)};r.render=function t(){this.l(this.props.ability);this.i=this.v();return this.props.passThrough||this.i?this.h():null};r.h=function t(){var n=this.props,r=n.children,e=n.ability;var i=typeof r==="function"?r(this.i,e):r;return i};return e(n,[{key:"allowed",get:function t(){return this.i}}])}(n.PureComponent);function l(t){return function(r){return n.createElement(t,{children:function t(e){return n.createElement(s,i({},r,{ability:r.ability||e}))}})}}function useAbility(t){var r=n.useContext(t);var e=n.useCallback(function(t){return r.on("updated",t)},[r]);var i=n.useCallback(function(){return r.rules},[r]);n.useSyncExternalStore(e,i,i);return r}t.Can=s;t.createContextualCan=l;t.useAbility=useAbility});
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
package/dist/umd/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/Can.ts","../../src/factory.ts","../../src/hooks/useAbility.ts"],"sourcesContent":["import { PureComponent, ReactNode } from 'react';\nimport {\n Unsubscribe,\n AbilityTuple,\n SubjectType,\n AnyAbility,\n Generics,\n Abilities,\n IfString,\n} from '@casl/ability';\n\nconst noop = () => {};\n\ntype AbilityCanProps<\n T extends Abilities,\n Else = IfString<T, { do: T } | { I: T }>\n> = T extends AbilityTuple\n ? { do: T[0], on: T[1], field?: string } |\n { I: T[0], a: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], an: Extract<T[1], SubjectType>, field?: string } |\n { I: T[0], this: Exclude<T[1], SubjectType>, field?: string }\n : Else;\n\ninterface ExtraProps {\n not?: boolean\n passThrough?: boolean\n}\n\ninterface CanExtraProps<T extends AnyAbility> extends ExtraProps {\n ability: T\n children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode)\n}\n\ninterface BoundCanExtraProps<T extends AnyAbility> extends ExtraProps {\n ability?: T\n children: ReactNode | ((isAllowed: boolean, ability: T) => ReactNode)\n}\n\nexport type CanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & CanExtraProps<T>;\nexport type BoundCanProps<T extends AnyAbility> =\n AbilityCanProps<Generics<T>['abilities']> & BoundCanExtraProps<T>;\n\nexport class Can<T extends AnyAbility> extends PureComponent<CanProps<T>, { t: boolean }> {\n private _isAllowed = false;\n private _ability: T | null = null;\n private _unsubscribeFromAbility: Unsubscribe = noop;\n state = { t: true }\n\n componentWillUnmount() {\n this._unsubscribeFromAbility();\n }\n\n private _connectToAbility(ability?: T) {\n if (ability === this._ability) {\n return;\n }\n\n this._unsubscribeFromAbility();\n this._ability = null;\n\n if (ability) {\n this._ability = ability;\n this._unsubscribeFromAbility = ability.on('updated', () => this.setState({ t: !this.state.t }));\n }\n }\n\n get allowed() {\n return this._isAllowed;\n }\n\n private _canRender(): boolean {\n const props: any = this.props;\n const subject = props.of || props.a || props.an || props.this || props.on;\n const can = props.not ? 'cannot' : 'can';\n\n return props.ability[can](props.I || props.do, subject, props.field);\n }\n\n render() {\n this._connectToAbility(this.props.ability);\n this._isAllowed = this._canRender();\n return this.props.passThrough || this._isAllowed ? this._renderChildren() : null;\n }\n\n private _renderChildren() {\n const { children, ability } = this.props;\n const elements = typeof children === 'function'\n ? children(this._isAllowed, ability as any)\n : children;\n\n return elements as ReactNode;\n }\n}\n","import { AnyAbility } from '@casl/ability';\nimport { Consumer, FunctionComponent, createElement } from 'react';\nimport { BoundCanProps, Can } from './Can';\n\nexport function createContextualCan<T extends AnyAbility>(\n Getter: Consumer<T>\n): FunctionComponent<BoundCanProps<T>> {\n return (props: BoundCanProps<T>) => createElement(Getter, {\n children: (ability: T) => \n createElement(Can, { ...props, ability: props.ability || ability } as any),\n });\n}\n","import React from 'react';\nimport { AnyAbility } from '@casl/ability';\n\nexport function useAbility<T extends AnyAbility>(context: React.Context<T>): T {\n const ability = React.useContext<T>(context);\n\n const subscribe = React.useCallback(\n (callback: () => void) => ability.on('updated', callback),\n [ability],\n );\n\n const getSnapshot = React.useCallback(() => ability.rules, [ability]);\n\n React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n return ability;\n}\n"],"names":["noop","Can","_PureComponent","_this","_len","arguments","length","args","Array","_key","call","apply","this","concat","_isAllowed","_ability","_unsubscribeFromAbility","state","t","_inheritsLoose","_proto","prototype","componentWillUnmount","_connectToAbility","ability","_this2","on","setState","_canRender","props","subject","of","a","an","can","not","I","do","field","render","passThrough","_renderChildren","_this$props","children","elements","_createClass","key","get","PureComponent","createContextualCan","Getter","createElement","_extends","useAbility","context","React","useContext","subscribe","useCallback","callback","getSnapshot","rules","useSyncExternalStore"],"mappings":"mxCAWA,IAAMA,EAAO,SAAPA,IAAc,EAgCpB,IAAaC,WAAGC,GAAA,SAAAD,IAAA,IAAAE,EAAA,IAAA,IAAAC,EAAAC,UAAAC,OAAAC,EAAA,IAAAC,MAAAJ,GAAAK,EAAA,EAAAA,EAAAL,EAAAK,IAAAF,EAAAE,GAAAJ,UAAAI,GAAAN,EAAAD,EAAAQ,KAAAC,MAAAT,EAAA,CAAAU,MAAAC,OAAAN,KAAAK,KAAAT,EACNW,EAAa,MAAKX,EAClBY,EAAqB,KAAIZ,EACzBa,EAAuChB,EAAIG,EACnDc,MAAQ,CAAEC,EAAG,MAAM,OAAAf,CAAA,CAAAgB,EAAAlB,EAAAC,GAAA,IAAAkB,EAAAnB,EAAAoB,UAAAD,EAEnBE,qBAAA,SAAAA,IACEV,KAAKI,GACP,EAACI,EAEOG,EAAR,SAAQA,kBAAkBC,GAAa,IAAAC,EAAAb,KACrC,GAAIY,IAAYZ,KAAKG,EACnB,OAGFH,KAAKI,IACLJ,KAAKG,EAAW,KAEhB,GAAIS,EAAS,CACXZ,KAAKG,EAAWS,EAChBZ,KAAKI,EAA0BQ,EAAQE,GAAG,UAAW,WAAA,OAAMD,EAAKE,SAAS,CAAET,GAAIO,EAAKR,MAAMC,GAAI,EAChG,CACF,EAACE,EAMOQ,EAAR,SAAQA,IACN,IAAMC,EAAajB,KAAKiB,MACxB,IAAMC,EAAUD,EAAME,IAAMF,EAAMG,GAAKH,EAAMI,IAAMJ,EAAMjB,MAAQiB,EAAMH,GACvE,IAAMQ,EAAML,EAAMM,IAAM,SAAW,MAEnC,OAAON,EAAML,QAAQU,GAAKL,EAAMO,GAAKP,EAAMQ,GAAIP,EAASD,EAAMS,MAChE,EAAClB,EAEDmB,OAAA,SAAAA,IACE3B,KAAKW,EAAkBX,KAAKiB,MAAML,SAClCZ,KAAKE,EAAaF,KAAKgB,IACvB,OAAOhB,KAAKiB,MAAMW,aAAe5B,KAAKE,EAAaF,KAAK6B,IAAoB,IAC9E,EAACrB,EAEOqB,EAAR,SAAQA,IACN,IAAAC,EAA8B9B,KAAKiB,MAA3Bc,EAAQD,EAARC,SAAUnB,EAAOkB,EAAPlB,QAClB,IAAMoB,SAAkBD,IAAa,WACjCA,EAAS/B,KAAKE,EAAYU,GAC1BmB,EAEJ,OAAOC,CACT,EAAC,OAAAC,EAAA5C,EAAA,CAAA,CAAA6C,IAAA,UAAAC,IAzBD,SAAAA,IACE,OAAOnC,KAAKE,CACd,IAAC,EA1B4CkC,EAAAA,eCvCxC,SAASC,EACdC,GAEA,OAAO,SAACrB,GAAuB,OAAKsB,EAAAA,cAAcD,EAAQ,CACxDP,SAAU,SAAVA,EAAWnB,GAAU,OACnB2B,EAAAA,cAAclD,EAAGmD,KAAOvB,EAAK,CAAEL,QAASK,EAAML,SAAWA,IAAiB,GAC5E,CACJ,CCRO,SAAS6B,WAAiCC,GAC/C,IAAM9B,EAAU+B,EAAMC,WAAcF,GAEpC,IAAMG,EAAYF,EAAMG,YACtB,SAACC,GAAoB,OAAKnC,EAAQE,GAAG,UAAWiC,EAAS,EACzD,CAACnC,IAGH,IAAMoC,EAAcL,EAAMG,YAAY,WAAA,OAAMlC,EAAQqC,KAAK,EAAE,CAACrC,IAE5D+B,EAAMO,qBAAqBL,EAAWG,EAAaA,GAEnD,OAAOpC,CACT"}
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dist/types';
|