@aglyn/shared-ui-jsx 1.0.0-beta.150 → 1.0.0-beta.151

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/shared-ui-jsx",
3
- "version": "1.0.0-beta.150",
3
+ "version": "1.0.0-beta.151",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,10 +25,10 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/shared-data-mdi": "1.0.0-beta.150",
29
- "@aglyn/shared-ui-theme": "1.0.0-beta.150",
30
- "@aglyn/shared-util-tools": "1.0.0-beta.150",
31
- "@aglyn/shared-util-vendor": "1.0.0-beta.150",
28
+ "@aglyn/shared-data-mdi": "1.0.0-beta.151",
29
+ "@aglyn/shared-ui-theme": "1.0.0-beta.151",
30
+ "@aglyn/shared-util-tools": "1.0.0-beta.151",
31
+ "@aglyn/shared-util-vendor": "1.0.0-beta.151",
32
32
  "@mdi/js": "^7.4.47",
33
33
  "@swc/helpers": "0.5.23",
34
34
  "change-case": "^5.4.4",
@@ -0,0 +1,52 @@
1
+ import { type ReactElement } from 'react';
2
+ export interface BlockedControlProps {
3
+ /**
4
+ * Whether the viewer's standing is RESOLVED and refused. Never true while
5
+ * a claim is still loading: rendering the refusal in that window flashes a
6
+ * disabled button at everybody, including the people who may use it.
7
+ */
8
+ blocked: boolean;
9
+ /** Why, in one sentence. Shown on hover, and to a screen reader. */
10
+ reason: string;
11
+ /**
12
+ * A single control that accepts a `disabled` prop — MUI's Button,
13
+ * IconButton, Switch, TextField and friends all do.
14
+ */
15
+ children: ReactElement<{
16
+ disabled?: boolean;
17
+ }>;
18
+ }
19
+ /**
20
+ * Renders a control disabled with the reason attached, or passes it through
21
+ * untouched.
22
+ *
23
+ * DISABLED WITH THE REASON, NOT HIDDEN. Both are defensible and the choice is
24
+ * made here once rather than at every gate. Hiding leaves a reader unable to
25
+ * see that the capability exists at all, and the commonest support act is
26
+ * routing — "this needs someone with the super role" is an answer they can
27
+ * give in one message; "I see no such button" turns into an investigation of
28
+ * whether the feature shipped.
29
+ *
30
+ * ## Two details that are the whole reason this is shared
31
+ *
32
+ * The tooltip wraps a `span`, not the control: MUI does not fire pointer
33
+ * events on a disabled button, so a tooltip attached directly to one never
34
+ * appears — which would leave a dead button and no reason at all, the exact
35
+ * failure this exists to stop.
36
+ *
37
+ * And the reason lands on the span as a real `aria-label` and `title`, not
38
+ * only inside the Tooltip's popper. An explanation that appears only on
39
+ * hover says nothing to a screen reader and nothing on a touch device.
40
+ *
41
+ * Both were got right once and then copied; AGL-2113 is the precedent for
42
+ * what copies do — five quota readouts grew their own phrasing and stopped
43
+ * agreeing. THIS IS NOT A SECURITY BOUNDARY: the server refuses regardless
44
+ * of what rendered. It exists so a console stops promising what the server
45
+ * will refuse.
46
+ */
47
+ export declare function BlockedControl({ blocked, reason, children }: BlockedControlProps): import("react").JSX.Element;
48
+ export declare namespace BlockedControl {
49
+ var displayName: string;
50
+ var aglyn: boolean;
51
+ }
52
+ export default BlockedControl;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx } from "react/jsx-runtime";
18
+ import { Box, Tooltip } from "@mui/material";
19
+ import { cloneElement, isValidElement } from "react";
20
+ /**
21
+ * Renders a control disabled with the reason attached, or passes it through
22
+ * untouched.
23
+ *
24
+ * DISABLED WITH THE REASON, NOT HIDDEN. Both are defensible and the choice is
25
+ * made here once rather than at every gate. Hiding leaves a reader unable to
26
+ * see that the capability exists at all, and the commonest support act is
27
+ * routing — "this needs someone with the super role" is an answer they can
28
+ * give in one message; "I see no such button" turns into an investigation of
29
+ * whether the feature shipped.
30
+ *
31
+ * ## Two details that are the whole reason this is shared
32
+ *
33
+ * The tooltip wraps a `span`, not the control: MUI does not fire pointer
34
+ * events on a disabled button, so a tooltip attached directly to one never
35
+ * appears — which would leave a dead button and no reason at all, the exact
36
+ * failure this exists to stop.
37
+ *
38
+ * And the reason lands on the span as a real `aria-label` and `title`, not
39
+ * only inside the Tooltip's popper. An explanation that appears only on
40
+ * hover says nothing to a screen reader and nothing on a touch device.
41
+ *
42
+ * Both were got right once and then copied; AGL-2113 is the precedent for
43
+ * what copies do — five quota readouts grew their own phrasing and stopped
44
+ * agreeing. THIS IS NOT A SECURITY BOUNDARY: the server refuses regardless
45
+ * of what rendered. It exists so a console stops promising what the server
46
+ * will refuse.
47
+ */ export function BlockedControl({ blocked, reason, children }) {
48
+ if (!blocked || !/*#__PURE__*/ isValidElement(children)) return children;
49
+ return /*#__PURE__*/ _jsx(Tooltip, {
50
+ title: reason,
51
+ children: /*#__PURE__*/ _jsx(Box, {
52
+ component: "span",
53
+ "aria-label": reason,
54
+ title: reason,
55
+ sx: {
56
+ display: 'inline-flex'
57
+ },
58
+ children: /*#__PURE__*/ cloneElement(children, {
59
+ disabled: true
60
+ })
61
+ })
62
+ });
63
+ }
64
+ BlockedControl.displayName = 'BlockedControl';
65
+ BlockedControl.aglyn = true;
66
+ export default BlockedControl;
67
+
68
+ //# sourceMappingURL=blocked-control.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/components/blocked-control.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { Box, Tooltip } from '@mui/material'\nimport { cloneElement, isValidElement, type ReactElement } from 'react'\n\nexport interface BlockedControlProps {\n /**\n * Whether the viewer's standing is RESOLVED and refused. Never true while\n * a claim is still loading: rendering the refusal in that window flashes a\n * disabled button at everybody, including the people who may use it.\n */\n blocked: boolean\n /** Why, in one sentence. Shown on hover, and to a screen reader. */\n reason: string\n /**\n * A single control that accepts a `disabled` prop — MUI's Button,\n * IconButton, Switch, TextField and friends all do.\n */\n children: ReactElement<{ disabled?: boolean }>\n}\n\n/**\n * Renders a control disabled with the reason attached, or passes it through\n * untouched.\n *\n * DISABLED WITH THE REASON, NOT HIDDEN. Both are defensible and the choice is\n * made here once rather than at every gate. Hiding leaves a reader unable to\n * see that the capability exists at all, and the commonest support act is\n * routing — \"this needs someone with the super role\" is an answer they can\n * give in one message; \"I see no such button\" turns into an investigation of\n * whether the feature shipped.\n *\n * ## Two details that are the whole reason this is shared\n *\n * The tooltip wraps a `span`, not the control: MUI does not fire pointer\n * events on a disabled button, so a tooltip attached directly to one never\n * appears — which would leave a dead button and no reason at all, the exact\n * failure this exists to stop.\n *\n * And the reason lands on the span as a real `aria-label` and `title`, not\n * only inside the Tooltip's popper. An explanation that appears only on\n * hover says nothing to a screen reader and nothing on a touch device.\n *\n * Both were got right once and then copied; AGL-2113 is the precedent for\n * what copies do — five quota readouts grew their own phrasing and stopped\n * agreeing. THIS IS NOT A SECURITY BOUNDARY: the server refuses regardless\n * of what rendered. It exists so a console stops promising what the server\n * will refuse.\n */\nexport function BlockedControl({ blocked, reason, children }: BlockedControlProps) {\n if (!blocked || !isValidElement(children)) return children\n return (\n <Tooltip title={reason}>\n <Box\n component=\"span\"\n aria-label={reason}\n title={reason}\n sx={{ display: 'inline-flex' }}\n >\n {cloneElement(children, { disabled: true })}\n </Box>\n </Tooltip>\n )\n}\n\nBlockedControl.displayName = 'BlockedControl'\nBlockedControl.aglyn = true\n\nexport default BlockedControl\n"],"names":["Box","Tooltip","cloneElement","isValidElement","BlockedControl","blocked","reason","children","title","component","aria-label","sx","display","disabled","displayName","aglyn"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,GAAG,EAAEC,OAAO,QAAQ,gBAAe;AAC5C,SAASC,YAAY,EAAEC,cAAc,QAA2B,QAAO;AAkBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GACD,OAAO,SAASC,eAAe,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,EAAuB;IAC/E,IAAI,CAACF,WAAW,eAACF,eAAeI,WAAW,OAAOA;IAClD,qBACE,KAACN;QAAQO,OAAOF;kBACd,cAAA,KAACN;YACCS,WAAU;YACVC,cAAYJ;YACZE,OAAOF;YACPK,IAAI;gBAAEC,SAAS;YAAc;sBAE5BV,cAAAA,aAAaK,UAAU;gBAAEM,UAAU;YAAK;;;AAIjD;AAEAT,eAAeU,WAAW,GAAG;AAC7BV,eAAeW,KAAK,GAAG;AAEvB,eAAeX,eAAc"}