@dxos/react-ui-attention 0.5.9-main.17d92ec
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/LICENSE +8 -0
- package/README.md +3 -0
- package/dist/lib/browser/index.mjs +131 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/types/src/components/AttentionGlyph.d.ts +9 -0
- package/dist/types/src/components/AttentionGlyph.d.ts.map +1 -0
- package/dist/types/src/components/AttentionGlyph.stories.d.ts +27 -0
- package/dist/types/src/components/AttentionGlyph.stories.d.ts.map +1 -0
- package/dist/types/src/components/AttentionProvider.d.ts +22 -0
- package/dist/types/src/components/AttentionProvider.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +3 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/package.json +49 -0
- package/src/components/AttentionGlyph.stories.tsx +52 -0
- package/src/components/AttentionGlyph.tsx +89 -0
- package/src/components/AttentionProvider.tsx +102 -0
- package/src/components/index.ts +6 -0
- package/src/index.ts +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
Copyright (c) 2024 DXOS
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// packages/ui/react-ui-attention/src/components/AttentionGlyph.tsx
|
|
2
|
+
import { Minus } from "@phosphor-icons/react";
|
|
3
|
+
import React, { forwardRef } from "react";
|
|
4
|
+
import { focusRing, mx } from "@dxos/react-ui-theme";
|
|
5
|
+
var attentionGlyphStyles = "inline-block rounded-sm is-3 bs-3 bg-transparent fg-accent transition-colors [[aria-current]_&]:surface-unAccent [[aria-current]_&]:fg-inverse [[data-attention=true]_&]:surface-accent [[data-attention=true]_&]:fg-inverse [[aria-current][data-attention=true]_&]:surface-accent [[aria-current][data-attention=true]_&]:fg-inverse";
|
|
6
|
+
var closeIconStyles = "hidden is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:block group-[[aria-current]_&:focus]/attentionGlyphButton:block group-[[data-attention=true]_&:hover]/attentionGlyphButton:block group-[[data-attention=true]_&:focus]/attentionGlyphButton:block group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:block group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:block";
|
|
7
|
+
var presenceIconStyles = "is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:hidden group-[[aria-current]_&:focus]/attentionGlyphButton:hidden group-[[data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[data-attention=true]_&:focus]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:hidden";
|
|
8
|
+
var PresenceOne = () => {
|
|
9
|
+
return /* @__PURE__ */ React.createElement("svg", {
|
|
10
|
+
width: "12",
|
|
11
|
+
height: "12",
|
|
12
|
+
viewBox: "0 0 12 12",
|
|
13
|
+
className: presenceIconStyles,
|
|
14
|
+
fill: "none",
|
|
15
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
16
|
+
}, /* @__PURE__ */ React.createElement("circle", {
|
|
17
|
+
cx: "6",
|
|
18
|
+
cy: "6",
|
|
19
|
+
r: "2.5",
|
|
20
|
+
fill: "currentColor"
|
|
21
|
+
}));
|
|
22
|
+
};
|
|
23
|
+
var PresenceMany = () => {
|
|
24
|
+
return /* @__PURE__ */ React.createElement("svg", {
|
|
25
|
+
width: "12",
|
|
26
|
+
height: "12",
|
|
27
|
+
viewBox: "0 0 12 12",
|
|
28
|
+
className: presenceIconStyles,
|
|
29
|
+
fill: "none",
|
|
30
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
31
|
+
}, /* @__PURE__ */ React.createElement("path", {
|
|
32
|
+
d: "M6.75 8.27311C7.38815 7.72296 7.79212 6.90866 7.79212 6C7.79212 5.09134 7.38815 4.27704 6.75 3.72689C7.06722 3.58122 7.42019 3.5 7.79212 3.5C9.17283 3.5 10.2921 4.61929 10.2921 6C10.2921 7.38071 9.17283 8.5 7.79212 8.5C7.42019 8.5 7.06723 8.41878 6.75 8.27311Z",
|
|
33
|
+
fill: "currentColor"
|
|
34
|
+
}), /* @__PURE__ */ React.createElement("circle", {
|
|
35
|
+
cx: "4.25",
|
|
36
|
+
cy: "6",
|
|
37
|
+
r: "2.5",
|
|
38
|
+
fill: "currentColor"
|
|
39
|
+
}));
|
|
40
|
+
};
|
|
41
|
+
var AttentionGlyph = /* @__PURE__ */ forwardRef(({ presence, classNames, ...props }, forwardedRef) => {
|
|
42
|
+
return /* @__PURE__ */ React.createElement("span", {
|
|
43
|
+
role: "none",
|
|
44
|
+
...props,
|
|
45
|
+
className: mx(attentionGlyphStyles, classNames),
|
|
46
|
+
ref: forwardedRef
|
|
47
|
+
}, presence === "many" && /* @__PURE__ */ React.createElement(PresenceMany, null), presence === "one" && /* @__PURE__ */ React.createElement(PresenceOne, null));
|
|
48
|
+
});
|
|
49
|
+
var AttentionGlyphCloseButton = /* @__PURE__ */ forwardRef(({ presence, classNames, ...props }, forwardedRef) => {
|
|
50
|
+
return /* @__PURE__ */ React.createElement("button", {
|
|
51
|
+
...props,
|
|
52
|
+
className: mx(attentionGlyphStyles, focusRing, "group/attentionGlyphButton pointer-events-none [[aria-current]_&]:pointer-events-auto [[data-attention=true]_&]:pointer-events-auto [[aria-current][data-attention=true]_&]:pointer-events-auto", classNames),
|
|
53
|
+
ref: forwardedRef
|
|
54
|
+
}, presence === "many" && /* @__PURE__ */ React.createElement(PresenceMany, null), presence === "one" && /* @__PURE__ */ React.createElement(PresenceOne, null), /* @__PURE__ */ React.createElement(Minus, {
|
|
55
|
+
weight: "bold",
|
|
56
|
+
className: closeIconStyles
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// packages/ui/react-ui-attention/src/components/AttentionProvider.tsx
|
|
61
|
+
import { createContext } from "@radix-ui/react-context";
|
|
62
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
63
|
+
import React2 from "react";
|
|
64
|
+
var ATTENTION_NAME = "Attention";
|
|
65
|
+
var [AttentionContextProvider, useAttentionContext] = createContext(ATTENTION_NAME, {
|
|
66
|
+
attended: /* @__PURE__ */ new Set()
|
|
67
|
+
});
|
|
68
|
+
var useHasAttention = (attendableId) => {
|
|
69
|
+
const { attended } = useAttentionContext(ATTENTION_NAME);
|
|
70
|
+
return attendableId ? attended.has(attendableId) : false;
|
|
71
|
+
};
|
|
72
|
+
var useAttendable = (attendableId) => {
|
|
73
|
+
return {
|
|
74
|
+
"data-attendable-id": attendableId
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
var getAttendables = (selector, cursor, acc = []) => {
|
|
78
|
+
const closestAttendable = cursor.closest(selector);
|
|
79
|
+
if (!closestAttendable) {
|
|
80
|
+
return acc;
|
|
81
|
+
} else {
|
|
82
|
+
const attendableId = closestAttendable.getAttribute("data-attendable-id");
|
|
83
|
+
if (!attendableId) {
|
|
84
|
+
const trigger = document.querySelector(`[aria-controls="${closestAttendable.getAttribute("id")}"]`);
|
|
85
|
+
if (!trigger) {
|
|
86
|
+
return acc;
|
|
87
|
+
} else {
|
|
88
|
+
return getAttendables(selector, trigger, acc);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
acc.push(attendableId);
|
|
92
|
+
return !closestAttendable.parentElement ? acc : getAttendables(selector, closestAttendable.parentElement, acc);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var AttentionProvider = ({ children, attended: propsAttended, defaultAttended, onChangeAttend }) => {
|
|
97
|
+
const [attended = /* @__PURE__ */ new Set(), setAttended] = useControllableState({
|
|
98
|
+
prop: propsAttended,
|
|
99
|
+
defaultProp: defaultAttended,
|
|
100
|
+
onChange: onChangeAttend
|
|
101
|
+
});
|
|
102
|
+
const handleFocus = (event) => {
|
|
103
|
+
const selector = [
|
|
104
|
+
"[data-attendable-id]",
|
|
105
|
+
...Array.from(document.querySelectorAll("[aria-controls]")).map((el) => `[id="${el.getAttribute("aria-controls")}"]`)
|
|
106
|
+
].join(",");
|
|
107
|
+
const nextAttended = new Set(getAttendables(selector, event.target));
|
|
108
|
+
const [prev, next] = [
|
|
109
|
+
Array.from(attended),
|
|
110
|
+
Array.from(nextAttended)
|
|
111
|
+
];
|
|
112
|
+
(prev.length !== next.length || !!prev.find((id, index) => next[index] !== id)) && setAttended(nextAttended);
|
|
113
|
+
};
|
|
114
|
+
return /* @__PURE__ */ React2.createElement(AttentionContextProvider, {
|
|
115
|
+
attended
|
|
116
|
+
}, /* @__PURE__ */ React2.createElement("div", {
|
|
117
|
+
role: "none",
|
|
118
|
+
className: "contents",
|
|
119
|
+
onFocusCapture: handleFocus
|
|
120
|
+
}, children));
|
|
121
|
+
};
|
|
122
|
+
export {
|
|
123
|
+
ATTENTION_NAME,
|
|
124
|
+
AttentionGlyph,
|
|
125
|
+
AttentionGlyphCloseButton,
|
|
126
|
+
AttentionProvider,
|
|
127
|
+
useAttendable,
|
|
128
|
+
useAttentionContext,
|
|
129
|
+
useHasAttention
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/components/AttentionGlyph.tsx", "../../../src/components/AttentionProvider.tsx"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { Minus } from '@phosphor-icons/react';\nimport { type Primitive } from '@radix-ui/react-primitive';\nimport React, { type ComponentPropsWithRef, forwardRef } from 'react';\n\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { focusRing, mx } from '@dxos/react-ui-theme';\n\nconst attentionGlyphStyles =\n 'inline-block rounded-sm is-3 bs-3 bg-transparent fg-accent transition-colors [[aria-current]_&]:surface-unAccent [[aria-current]_&]:fg-inverse [[data-attention=true]_&]:surface-accent [[data-attention=true]_&]:fg-inverse [[aria-current][data-attention=true]_&]:surface-accent [[aria-current][data-attention=true]_&]:fg-inverse';\n\nconst closeIconStyles =\n 'hidden is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:block group-[[aria-current]_&:focus]/attentionGlyphButton:block group-[[data-attention=true]_&:hover]/attentionGlyphButton:block group-[[data-attention=true]_&:focus]/attentionGlyphButton:block group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:block group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:block';\n\nconst presenceIconStyles =\n 'is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:hidden group-[[aria-current]_&:focus]/attentionGlyphButton:hidden group-[[data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[data-attention=true]_&:focus]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:hidden';\n\nconst PresenceOne = () => {\n return (\n <svg\n width='12'\n height='12'\n viewBox='0 0 12 12'\n className={presenceIconStyles}\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <circle cx='6' cy='6' r='2.5' fill='currentColor' />\n </svg>\n );\n};\n\nconst PresenceMany = () => {\n return (\n <svg\n width='12'\n height='12'\n viewBox='0 0 12 12'\n className={presenceIconStyles}\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M6.75 8.27311C7.38815 7.72296 7.79212 6.90866 7.79212 6C7.79212 5.09134 7.38815 4.27704 6.75 3.72689C7.06722 3.58122 7.42019 3.5 7.79212 3.5C9.17283 3.5 10.2921 4.61929 10.2921 6C10.2921 7.38071 9.17283 8.5 7.79212 8.5C7.42019 8.5 7.06723 8.41878 6.75 8.27311Z'\n fill='currentColor'\n />\n <circle cx='4.25' cy='6' r='2.5' fill='currentColor' />\n </svg>\n );\n};\n\nexport type AttentionGlyphProps = {\n presence?: 'none' | 'one' | 'many';\n} & ThemedClassName<Omit<ComponentPropsWithRef<typeof Primitive.span>, 'children'>>;\n\nexport const AttentionGlyph = forwardRef<HTMLSpanElement, AttentionGlyphProps>(\n ({ presence, classNames, ...props }, forwardedRef) => {\n return (\n <span role='none' {...props} className={mx(attentionGlyphStyles, classNames)} ref={forwardedRef}>\n {presence === 'many' && <PresenceMany />}\n {presence === 'one' && <PresenceOne />}\n </span>\n );\n },\n);\n\nexport const AttentionGlyphCloseButton = forwardRef<HTMLButtonElement, AttentionGlyphProps>(\n ({ presence, classNames, ...props }, forwardedRef) => {\n return (\n <button\n {...props}\n className={mx(\n attentionGlyphStyles,\n focusRing,\n 'group/attentionGlyphButton pointer-events-none [[aria-current]_&]:pointer-events-auto [[data-attention=true]_&]:pointer-events-auto [[aria-current][data-attention=true]_&]:pointer-events-auto',\n classNames,\n )}\n ref={forwardedRef}\n >\n {presence === 'many' && <PresenceMany />}\n {presence === 'one' && <PresenceOne />}\n <Minus weight='bold' className={closeIconStyles} />\n </button>\n );\n },\n);\n", "//\n// Copyright 2024 DXOS.org\n//\n\n// NOTE(thure): The following unused imports quell TS2742 (“likely not portable”).\n\n// eslint-disable-next-line unused-imports/no-unused-imports\nimport { createContext, type Scope, type CreateScope } from '@radix-ui/react-context';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\n// eslint-disable-next-line unused-imports/no-unused-imports\nimport React, { ComponentProps, type FocusEvent, type HTMLAttributes, type PropsWithChildren, useState } from 'react';\n\nconst ATTENTION_NAME = 'Attention';\n\ntype AttentionContextValue = {\n attended: Set<string>;\n};\n\nconst [AttentionContextProvider, useAttentionContext] = createContext<AttentionContextValue>(ATTENTION_NAME, {\n attended: new Set(),\n});\n\nconst useHasAttention = (attendableId?: string) => {\n const { attended } = useAttentionContext(ATTENTION_NAME);\n return attendableId ? attended.has(attendableId) : false;\n};\n\n/**\n * Computes HTML element attributes to apply so the attention system can detect changes\n * @param attendableId\n */\nconst useAttendable = (attendableId?: string) => {\n return { 'data-attendable-id': attendableId };\n};\n\n/**\n * Accumulates all attendable id’s between the element provided and the root, inclusive.\n */\nconst getAttendables = (selector: string, cursor: Element, acc: string[] = []): string[] => {\n // Find the closest element with `data-attendable-id`, if any; start from cursor and move up the DOM tree.\n const closestAttendable = cursor.closest(selector);\n if (!closestAttendable) {\n return acc;\n } else {\n const attendableId = closestAttendable.getAttribute('data-attendable-id');\n if (!attendableId) {\n // this has an id of an aria-controls elsewhere on the page, move cursor to that trigger\n const trigger = document.querySelector(`[aria-controls=\"${closestAttendable.getAttribute('id')}\"]`);\n if (!trigger) {\n return acc;\n } else {\n return getAttendables(selector, trigger, acc);\n }\n } else {\n acc.push(attendableId);\n // (attempt tail recursion)\n return !closestAttendable.parentElement ? acc : getAttendables(selector, closestAttendable.parentElement, acc);\n }\n }\n};\n\nconst AttentionProvider = ({\n children,\n attended: propsAttended,\n defaultAttended,\n onChangeAttend,\n}: PropsWithChildren<\n Partial<{\n attended: Set<string>;\n onChangeAttend: (nextAttended: Set<string>) => void;\n defaultAttended: Set<string>;\n }>\n>) => {\n const [attended = new Set(), setAttended] = useControllableState<Set<string>>({\n prop: propsAttended,\n defaultProp: defaultAttended,\n onChange: onChangeAttend,\n });\n const handleFocus = (event: FocusEvent) => {\n const selector = [\n '[data-attendable-id]',\n ...Array.from(document.querySelectorAll('[aria-controls]')).map(\n (el) => `[id=\"${el.getAttribute('aria-controls')}\"]`,\n ),\n ].join(',');\n const nextAttended = new Set(getAttendables(selector, event.target));\n const [prev, next] = [Array.from(attended), Array.from(nextAttended)];\n // Only update state if the result is different.\n (prev.length !== next.length || !!prev.find((id, index) => next[index] !== id)) && setAttended(nextAttended);\n };\n return (\n <AttentionContextProvider attended={attended}>\n <div role='none' className='contents' onFocusCapture={handleFocus}>\n {children}\n </div>\n </AttentionContextProvider>\n );\n};\n\nexport { AttentionProvider, useAttentionContext, useHasAttention, useAttendable, ATTENTION_NAME };\n\nexport type { AttentionContextValue };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Minus", "React", "forwardRef", "focusRing", "mx", "attentionGlyphStyles", "closeIconStyles", "presenceIconStyles", "PresenceOne", "svg", "width", "height", "viewBox", "className", "fill", "xmlns", "circle", "cx", "cy", "r", "PresenceMany", "path", "d", "AttentionGlyph", "forwardRef", "presence", "classNames", "props", "forwardedRef", "span", "role", "mx", "ref", "AttentionGlyphCloseButton", "button", "focusRing", "Minus", "weight", "createContext", "useControllableState", "React", "ATTENTION_NAME", "AttentionContextProvider", "useAttentionContext", "createContext", "attended", "Set", "useHasAttention", "attendableId", "has", "useAttendable", "getAttendables", "selector", "cursor", "acc", "closestAttendable", "closest", "getAttribute", "trigger", "document", "querySelector", "push", "parentElement", "AttentionProvider", "children", "propsAttended", "defaultAttended", "onChangeAttend", "setAttended", "useControllableState", "prop", "defaultProp", "onChange", "handleFocus", "event", "Array", "from", "querySelectorAll", "map", "el", "join", "nextAttended", "target", "prev", "next", "length", "find", "id", "index", "React", "div", "role", "className", "onFocusCapture"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"packages/ui/react-ui-attention/src/components/AttentionGlyph.tsx":{"bytes":11245,"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true}],"format":"esm"},"packages/ui/react-ui-attention/src/components/AttentionProvider.tsx":{"bytes":11903,"imports":[{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"packages/ui/react-ui-attention/src/components/index.ts":{"bytes":644,"imports":[{"path":"packages/ui/react-ui-attention/src/components/AttentionGlyph.tsx","kind":"import-statement","original":"./AttentionGlyph"},{"path":"packages/ui/react-ui-attention/src/components/AttentionProvider.tsx","kind":"import-statement","original":"./AttentionProvider"}],"format":"esm"},"packages/ui/react-ui-attention/src/index.ts":{"bytes":508,"imports":[{"path":"packages/ui/react-ui-attention/src/components/index.ts","kind":"import-statement","original":"./components"}],"format":"esm"}},"outputs":{"packages/ui/react-ui-attention/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":11629},"packages/ui/react-ui-attention/dist/lib/browser/index.mjs":{"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true}],"exports":["ATTENTION_NAME","AttentionGlyph","AttentionGlyphCloseButton","AttentionProvider","useAttendable","useAttentionContext","useHasAttention"],"entryPoint":"packages/ui/react-ui-attention/src/index.ts","inputs":{"packages/ui/react-ui-attention/src/components/AttentionGlyph.tsx":{"bytesInOutput":3642},"packages/ui/react-ui-attention/src/components/index.ts":{"bytesInOutput":0},"packages/ui/react-ui-attention/src/components/AttentionProvider.tsx":{"bytesInOutput":2289},"packages/ui/react-ui-attention/src/index.ts":{"bytesInOutput":0}},"bytes":6262}}}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Primitive } from '@radix-ui/react-primitive';
|
|
2
|
+
import React, { type ComponentPropsWithRef } from 'react';
|
|
3
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
4
|
+
export type AttentionGlyphProps = {
|
|
5
|
+
presence?: 'none' | 'one' | 'many';
|
|
6
|
+
} & ThemedClassName<Omit<ComponentPropsWithRef<typeof Primitive.span>, 'children'>>;
|
|
7
|
+
export declare const AttentionGlyph: React.ForwardRefExoticComponent<Pick<AttentionGlyphProps, "slot" | "style" | "title" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "asChild" | "presence" | "classNames"> & React.RefAttributes<HTMLSpanElement>>;
|
|
8
|
+
export declare const AttentionGlyphCloseButton: React.ForwardRefExoticComponent<Pick<AttentionGlyphProps, "slot" | "style" | "title" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "asChild" | "presence" | "classNames"> & React.RefAttributes<HTMLButtonElement>>;
|
|
9
|
+
//# sourceMappingURL=AttentionGlyph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttentionGlyph.d.ts","sourceRoot":"","sources":["../../../../src/components/AttentionGlyph.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,EAAE,KAAK,qBAAqB,EAAc,MAAM,OAAO,CAAC;AAEtE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA8CtD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CACpC,GAAG,eAAe,CAAC,IAAI,CAAC,qBAAqB,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AAEpF,eAAO,MAAM,cAAc,smJAS1B,CAAC;AAEF,eAAO,MAAM,yBAAyB,wmJAmBrC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import '@dxosTheme';
|
|
3
|
+
type StorybookAttentionGlyphProps = {
|
|
4
|
+
current?: boolean;
|
|
5
|
+
attention?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const _default: {
|
|
8
|
+
title: string;
|
|
9
|
+
component: ({ current, attention }: StorybookAttentionGlyphProps) => JSX.Element;
|
|
10
|
+
decorators: import("@storybook/react/*").Decorator[];
|
|
11
|
+
};
|
|
12
|
+
export default _default;
|
|
13
|
+
export declare const Default: {
|
|
14
|
+
args: {};
|
|
15
|
+
};
|
|
16
|
+
export declare const Current: {
|
|
17
|
+
args: {
|
|
18
|
+
current: boolean;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare const Attention: {
|
|
22
|
+
args: {
|
|
23
|
+
current: boolean;
|
|
24
|
+
attention: boolean;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=AttentionGlyph.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttentionGlyph.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/AttentionGlyph.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;AAQpB,KAAK,4BAA4B,GAAG;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;;;;;;AAsBF,wBAIE;AAEF,eAAO,MAAM,OAAO;;CAEnB,CAAC;AACF,eAAO,MAAM,OAAO;;;;CAEnB,CAAC;AACF,eAAO,MAAM,SAAS;;;;;CAErB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type PropsWithChildren } from 'react';
|
|
2
|
+
declare const ATTENTION_NAME = "Attention";
|
|
3
|
+
type AttentionContextValue = {
|
|
4
|
+
attended: Set<string>;
|
|
5
|
+
};
|
|
6
|
+
declare const useAttentionContext: (consumerName: string) => AttentionContextValue;
|
|
7
|
+
declare const useHasAttention: (attendableId?: string) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Computes HTML element attributes to apply so the attention system can detect changes
|
|
10
|
+
* @param attendableId
|
|
11
|
+
*/
|
|
12
|
+
declare const useAttendable: (attendableId?: string) => {
|
|
13
|
+
'data-attendable-id': string | undefined;
|
|
14
|
+
};
|
|
15
|
+
declare const AttentionProvider: ({ children, attended: propsAttended, defaultAttended, onChangeAttend, }: PropsWithChildren<Partial<{
|
|
16
|
+
attended: Set<string>;
|
|
17
|
+
onChangeAttend: (nextAttended: Set<string>) => void;
|
|
18
|
+
defaultAttended: Set<string>;
|
|
19
|
+
}>>) => JSX.Element;
|
|
20
|
+
export { AttentionProvider, useAttentionContext, useHasAttention, useAttendable, ATTENTION_NAME };
|
|
21
|
+
export type { AttentionContextValue };
|
|
22
|
+
//# sourceMappingURL=AttentionProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttentionProvider.d.ts","sourceRoot":"","sources":["../../../../src/components/AttentionProvider.tsx"],"names":[],"mappings":"AAUA,OAAc,EAAwD,KAAK,iBAAiB,EAAY,MAAM,OAAO,CAAC;AAEtH,QAAA,MAAM,cAAc,cAAc,CAAC;AAEnC,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB,CAAC;AAEF,QAAA,MAAiC,mBAAmB,iDAElD,CAAC;AAEH,QAAA,MAAM,eAAe,kBAAmB,MAAM,YAG7C,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,aAAa,kBAAmB,MAAM;;CAE3C,CAAC;AA4BF,QAAA,MAAM,iBAAiB,4EAKpB,kBACD,QAAQ;IACN,QAAQ,EAAE,IAAI,MAAM,CAAC,CAAC;IACtB,cAAc,EAAE,CAAC,YAAY,EAAE,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC;IACpD,eAAe,EAAE,IAAI,MAAM,CAAC,CAAC;CAC9B,CAAC,CACH,gBAyBA,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;AAElG,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAIA,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/react-ui-attention",
|
|
3
|
+
"version": "0.5.9-main.17d92ec",
|
|
4
|
+
"description": "Signifier components and providers for attention system.",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "DXOS.org",
|
|
9
|
+
"main": "dist/lib/node/index.cjs",
|
|
10
|
+
"browser": {
|
|
11
|
+
"./dist/lib/node/index.cjs": "./dist/lib/browser/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"types": "dist/types/src/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@radix-ui/primitive": "^1.0.0",
|
|
20
|
+
"@radix-ui/react-compose-refs": "^1.0.0",
|
|
21
|
+
"@radix-ui/react-context": "^1.0.0",
|
|
22
|
+
"@radix-ui/react-menu": "^2.0.6",
|
|
23
|
+
"@radix-ui/react-primitive": "^1.0.2",
|
|
24
|
+
"@radix-ui/react-slot": "^1.0.1",
|
|
25
|
+
"@radix-ui/react-use-controllable-state": "^1.0.0",
|
|
26
|
+
"@dxos/react-ui": "0.5.9-main.17d92ec",
|
|
27
|
+
"@dxos/react-ui-theme": "0.5.9-main.17d92ec",
|
|
28
|
+
"@dxos/util": "0.5.9-main.17d92ec"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@phosphor-icons/react": "^2.1.5",
|
|
32
|
+
"@types/react": "^18.0.21",
|
|
33
|
+
"@types/react-dom": "^18.0.6",
|
|
34
|
+
"react": "^18.2.0",
|
|
35
|
+
"react-dom": "^18.2.0",
|
|
36
|
+
"vite": "^5.2.9",
|
|
37
|
+
"@dxos/random": "0.5.9-main.17d92ec",
|
|
38
|
+
"@dxos/app-graph": "0.5.9-main.17d92ec",
|
|
39
|
+
"@dxos/storybook-utils": "0.5.9-main.17d92ec"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@phosphor-icons/react": "^2.1.5",
|
|
43
|
+
"react": "^18.2.0",
|
|
44
|
+
"react-dom": "^18.2.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
10
|
+
|
|
11
|
+
import { AttentionGlyph } from './AttentionGlyph';
|
|
12
|
+
|
|
13
|
+
type StorybookAttentionGlyphProps = {
|
|
14
|
+
current?: boolean;
|
|
15
|
+
attention?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const StorybookAttentionGlyph = ({ current, attention }: StorybookAttentionGlyphProps) => {
|
|
19
|
+
const itemAttrs = {
|
|
20
|
+
...(current && { 'aria-current': 'page' as const }),
|
|
21
|
+
...(attention && { 'data-attention': 'true' }),
|
|
22
|
+
};
|
|
23
|
+
return (
|
|
24
|
+
<ul className='flex gap-2 mbe-2'>
|
|
25
|
+
<li {...itemAttrs}>
|
|
26
|
+
<AttentionGlyph presence='none' />
|
|
27
|
+
</li>
|
|
28
|
+
<li {...itemAttrs}>
|
|
29
|
+
<AttentionGlyph presence='one' />
|
|
30
|
+
</li>
|
|
31
|
+
<li {...itemAttrs}>
|
|
32
|
+
<AttentionGlyph presence='many' />
|
|
33
|
+
</li>
|
|
34
|
+
</ul>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default {
|
|
39
|
+
title: 'react-ui-deck/AttentionGlyph',
|
|
40
|
+
component: StorybookAttentionGlyph,
|
|
41
|
+
decorators: [withTheme],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Default = {
|
|
45
|
+
args: {},
|
|
46
|
+
};
|
|
47
|
+
export const Current = {
|
|
48
|
+
args: { current: true },
|
|
49
|
+
};
|
|
50
|
+
export const Attention = {
|
|
51
|
+
args: { current: true, attention: true },
|
|
52
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Minus } from '@phosphor-icons/react';
|
|
6
|
+
import { type Primitive } from '@radix-ui/react-primitive';
|
|
7
|
+
import React, { type ComponentPropsWithRef, forwardRef } from 'react';
|
|
8
|
+
|
|
9
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
+
import { focusRing, mx } from '@dxos/react-ui-theme';
|
|
11
|
+
|
|
12
|
+
const attentionGlyphStyles =
|
|
13
|
+
'inline-block rounded-sm is-3 bs-3 bg-transparent fg-accent transition-colors [[aria-current]_&]:surface-unAccent [[aria-current]_&]:fg-inverse [[data-attention=true]_&]:surface-accent [[data-attention=true]_&]:fg-inverse [[aria-current][data-attention=true]_&]:surface-accent [[aria-current][data-attention=true]_&]:fg-inverse';
|
|
14
|
+
|
|
15
|
+
const closeIconStyles =
|
|
16
|
+
'hidden is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:block group-[[aria-current]_&:focus]/attentionGlyphButton:block group-[[data-attention=true]_&:hover]/attentionGlyphButton:block group-[[data-attention=true]_&:focus]/attentionGlyphButton:block group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:block group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:block';
|
|
17
|
+
|
|
18
|
+
const presenceIconStyles =
|
|
19
|
+
'is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:hidden group-[[aria-current]_&:focus]/attentionGlyphButton:hidden group-[[data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[data-attention=true]_&:focus]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:hidden';
|
|
20
|
+
|
|
21
|
+
const PresenceOne = () => {
|
|
22
|
+
return (
|
|
23
|
+
<svg
|
|
24
|
+
width='12'
|
|
25
|
+
height='12'
|
|
26
|
+
viewBox='0 0 12 12'
|
|
27
|
+
className={presenceIconStyles}
|
|
28
|
+
fill='none'
|
|
29
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
30
|
+
>
|
|
31
|
+
<circle cx='6' cy='6' r='2.5' fill='currentColor' />
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const PresenceMany = () => {
|
|
37
|
+
return (
|
|
38
|
+
<svg
|
|
39
|
+
width='12'
|
|
40
|
+
height='12'
|
|
41
|
+
viewBox='0 0 12 12'
|
|
42
|
+
className={presenceIconStyles}
|
|
43
|
+
fill='none'
|
|
44
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
45
|
+
>
|
|
46
|
+
<path
|
|
47
|
+
d='M6.75 8.27311C7.38815 7.72296 7.79212 6.90866 7.79212 6C7.79212 5.09134 7.38815 4.27704 6.75 3.72689C7.06722 3.58122 7.42019 3.5 7.79212 3.5C9.17283 3.5 10.2921 4.61929 10.2921 6C10.2921 7.38071 9.17283 8.5 7.79212 8.5C7.42019 8.5 7.06723 8.41878 6.75 8.27311Z'
|
|
48
|
+
fill='currentColor'
|
|
49
|
+
/>
|
|
50
|
+
<circle cx='4.25' cy='6' r='2.5' fill='currentColor' />
|
|
51
|
+
</svg>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type AttentionGlyphProps = {
|
|
56
|
+
presence?: 'none' | 'one' | 'many';
|
|
57
|
+
} & ThemedClassName<Omit<ComponentPropsWithRef<typeof Primitive.span>, 'children'>>;
|
|
58
|
+
|
|
59
|
+
export const AttentionGlyph = forwardRef<HTMLSpanElement, AttentionGlyphProps>(
|
|
60
|
+
({ presence, classNames, ...props }, forwardedRef) => {
|
|
61
|
+
return (
|
|
62
|
+
<span role='none' {...props} className={mx(attentionGlyphStyles, classNames)} ref={forwardedRef}>
|
|
63
|
+
{presence === 'many' && <PresenceMany />}
|
|
64
|
+
{presence === 'one' && <PresenceOne />}
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
export const AttentionGlyphCloseButton = forwardRef<HTMLButtonElement, AttentionGlyphProps>(
|
|
71
|
+
({ presence, classNames, ...props }, forwardedRef) => {
|
|
72
|
+
return (
|
|
73
|
+
<button
|
|
74
|
+
{...props}
|
|
75
|
+
className={mx(
|
|
76
|
+
attentionGlyphStyles,
|
|
77
|
+
focusRing,
|
|
78
|
+
'group/attentionGlyphButton pointer-events-none [[aria-current]_&]:pointer-events-auto [[data-attention=true]_&]:pointer-events-auto [[aria-current][data-attention=true]_&]:pointer-events-auto',
|
|
79
|
+
classNames,
|
|
80
|
+
)}
|
|
81
|
+
ref={forwardedRef}
|
|
82
|
+
>
|
|
83
|
+
{presence === 'many' && <PresenceMany />}
|
|
84
|
+
{presence === 'one' && <PresenceOne />}
|
|
85
|
+
<Minus weight='bold' className={closeIconStyles} />
|
|
86
|
+
</button>
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// NOTE(thure): The following unused imports quell TS2742 (“likely not portable”).
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line unused-imports/no-unused-imports
|
|
8
|
+
import { createContext, type Scope, type CreateScope } from '@radix-ui/react-context';
|
|
9
|
+
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
10
|
+
// eslint-disable-next-line unused-imports/no-unused-imports
|
|
11
|
+
import React, { ComponentProps, type FocusEvent, type HTMLAttributes, type PropsWithChildren, useState } from 'react';
|
|
12
|
+
|
|
13
|
+
const ATTENTION_NAME = 'Attention';
|
|
14
|
+
|
|
15
|
+
type AttentionContextValue = {
|
|
16
|
+
attended: Set<string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const [AttentionContextProvider, useAttentionContext] = createContext<AttentionContextValue>(ATTENTION_NAME, {
|
|
20
|
+
attended: new Set(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const useHasAttention = (attendableId?: string) => {
|
|
24
|
+
const { attended } = useAttentionContext(ATTENTION_NAME);
|
|
25
|
+
return attendableId ? attended.has(attendableId) : false;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Computes HTML element attributes to apply so the attention system can detect changes
|
|
30
|
+
* @param attendableId
|
|
31
|
+
*/
|
|
32
|
+
const useAttendable = (attendableId?: string) => {
|
|
33
|
+
return { 'data-attendable-id': attendableId };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Accumulates all attendable id’s between the element provided and the root, inclusive.
|
|
38
|
+
*/
|
|
39
|
+
const getAttendables = (selector: string, cursor: Element, acc: string[] = []): string[] => {
|
|
40
|
+
// Find the closest element with `data-attendable-id`, if any; start from cursor and move up the DOM tree.
|
|
41
|
+
const closestAttendable = cursor.closest(selector);
|
|
42
|
+
if (!closestAttendable) {
|
|
43
|
+
return acc;
|
|
44
|
+
} else {
|
|
45
|
+
const attendableId = closestAttendable.getAttribute('data-attendable-id');
|
|
46
|
+
if (!attendableId) {
|
|
47
|
+
// this has an id of an aria-controls elsewhere on the page, move cursor to that trigger
|
|
48
|
+
const trigger = document.querySelector(`[aria-controls="${closestAttendable.getAttribute('id')}"]`);
|
|
49
|
+
if (!trigger) {
|
|
50
|
+
return acc;
|
|
51
|
+
} else {
|
|
52
|
+
return getAttendables(selector, trigger, acc);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
acc.push(attendableId);
|
|
56
|
+
// (attempt tail recursion)
|
|
57
|
+
return !closestAttendable.parentElement ? acc : getAttendables(selector, closestAttendable.parentElement, acc);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const AttentionProvider = ({
|
|
63
|
+
children,
|
|
64
|
+
attended: propsAttended,
|
|
65
|
+
defaultAttended,
|
|
66
|
+
onChangeAttend,
|
|
67
|
+
}: PropsWithChildren<
|
|
68
|
+
Partial<{
|
|
69
|
+
attended: Set<string>;
|
|
70
|
+
onChangeAttend: (nextAttended: Set<string>) => void;
|
|
71
|
+
defaultAttended: Set<string>;
|
|
72
|
+
}>
|
|
73
|
+
>) => {
|
|
74
|
+
const [attended = new Set(), setAttended] = useControllableState<Set<string>>({
|
|
75
|
+
prop: propsAttended,
|
|
76
|
+
defaultProp: defaultAttended,
|
|
77
|
+
onChange: onChangeAttend,
|
|
78
|
+
});
|
|
79
|
+
const handleFocus = (event: FocusEvent) => {
|
|
80
|
+
const selector = [
|
|
81
|
+
'[data-attendable-id]',
|
|
82
|
+
...Array.from(document.querySelectorAll('[aria-controls]')).map(
|
|
83
|
+
(el) => `[id="${el.getAttribute('aria-controls')}"]`,
|
|
84
|
+
),
|
|
85
|
+
].join(',');
|
|
86
|
+
const nextAttended = new Set(getAttendables(selector, event.target));
|
|
87
|
+
const [prev, next] = [Array.from(attended), Array.from(nextAttended)];
|
|
88
|
+
// Only update state if the result is different.
|
|
89
|
+
(prev.length !== next.length || !!prev.find((id, index) => next[index] !== id)) && setAttended(nextAttended);
|
|
90
|
+
};
|
|
91
|
+
return (
|
|
92
|
+
<AttentionContextProvider attended={attended}>
|
|
93
|
+
<div role='none' className='contents' onFocusCapture={handleFocus}>
|
|
94
|
+
{children}
|
|
95
|
+
</div>
|
|
96
|
+
</AttentionContextProvider>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export { AttentionProvider, useAttentionContext, useHasAttention, useAttendable, ATTENTION_NAME };
|
|
101
|
+
|
|
102
|
+
export type { AttentionContextValue };
|