jvetrau-ds 0.1.39 → 0.1.41
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/components/course-outline/course-outline.cjs +190 -0
- package/components/course-outline/course-outline.cjs.map +1 -0
- package/components/course-outline/course-outline.css +600 -0
- package/components/course-outline/course-outline.d.ts +26 -0
- package/components/course-outline/course-outline.js +184 -0
- package/components/course-outline/course-outline.js.map +1 -0
- package/components/course-outline/index.cjs +13 -0
- package/components/course-outline/index.cjs.map +1 -0
- package/components/course-outline/index.d.ts +2 -0
- package/components/course-outline/index.js +3 -0
- package/components/course-outline/index.js.map +1 -0
- package/components/discussion/discussion.cjs +63 -0
- package/components/discussion/discussion.cjs.map +1 -0
- package/components/discussion/discussion.css +167 -0
- package/components/discussion/discussion.d.ts +14 -0
- package/components/discussion/discussion.js +61 -0
- package/components/discussion/discussion.js.map +1 -0
- package/components/discussion/index.cjs +13 -0
- package/components/discussion/index.cjs.map +1 -0
- package/components/discussion/index.d.ts +2 -0
- package/components/discussion/index.js +3 -0
- package/components/discussion/index.js.map +1 -0
- package/components/index.cjs +12 -0
- package/components/index.d.ts +4 -0
- package/components/index.js +2 -0
- package/index.cjs +12 -0
- package/index.d.ts +4 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/styles.css +771 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
require('./course-outline.css');
|
|
6
|
+
var classnames = require('../helpers/classnames');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
11
|
+
|
|
12
|
+
function hasContent(value) {
|
|
13
|
+
return value !== null && value !== void 0;
|
|
14
|
+
}
|
|
15
|
+
function getInitialCollapsedGroups(groups) {
|
|
16
|
+
return new Map(
|
|
17
|
+
groups.map((group) => [
|
|
18
|
+
group.id,
|
|
19
|
+
Boolean(group.defaultCollapsed)
|
|
20
|
+
])
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function getGroupCollapsed(collapsedGroups, group) {
|
|
24
|
+
return collapsedGroups.get(group.id) ?? Boolean(group.defaultCollapsed);
|
|
25
|
+
}
|
|
26
|
+
const GroupTitle = ({
|
|
27
|
+
level,
|
|
28
|
+
children
|
|
29
|
+
}) => {
|
|
30
|
+
if (!level) {
|
|
31
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "course-outline__group-title", children });
|
|
32
|
+
}
|
|
33
|
+
switch (level) {
|
|
34
|
+
case 2:
|
|
35
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "course-outline__group-title", children });
|
|
36
|
+
case 3:
|
|
37
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "course-outline__group-title", children });
|
|
38
|
+
case 4:
|
|
39
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "course-outline__group-title", children });
|
|
40
|
+
case 5:
|
|
41
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h5", { className: "course-outline__group-title", children });
|
|
42
|
+
default:
|
|
43
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h6", { className: "course-outline__group-title", children });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const CourseOutline = ({
|
|
47
|
+
groups,
|
|
48
|
+
headingLevel,
|
|
49
|
+
collapsible = false,
|
|
50
|
+
className = "",
|
|
51
|
+
...props
|
|
52
|
+
}) => {
|
|
53
|
+
const visibleGroups = React__default.default.useMemo(
|
|
54
|
+
() => groups.filter(
|
|
55
|
+
(group) => group.items.length > 0
|
|
56
|
+
),
|
|
57
|
+
[groups]
|
|
58
|
+
);
|
|
59
|
+
const [collapsedGroups, setCollapsedGroups] = React__default.default.useState(
|
|
60
|
+
() => getInitialCollapsedGroups(visibleGroups)
|
|
61
|
+
);
|
|
62
|
+
const componentId = React__default.default.useId();
|
|
63
|
+
React__default.default.useEffect(() => {
|
|
64
|
+
if (!collapsible) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
setCollapsedGroups((current) => {
|
|
68
|
+
const next = new Map(current);
|
|
69
|
+
let changed = false;
|
|
70
|
+
for (const group of visibleGroups) {
|
|
71
|
+
if (!next.has(group.id)) {
|
|
72
|
+
next.set(
|
|
73
|
+
group.id,
|
|
74
|
+
Boolean(group.defaultCollapsed)
|
|
75
|
+
);
|
|
76
|
+
changed = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return changed ? next : current;
|
|
80
|
+
});
|
|
81
|
+
}, [collapsible, visibleGroups]);
|
|
82
|
+
if (visibleGroups.length === 0) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const classes = classnames.classnames(
|
|
86
|
+
"course-outline",
|
|
87
|
+
className,
|
|
88
|
+
{
|
|
89
|
+
"course-outline--collapsible": collapsible
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
const toggleGroup = (group) => {
|
|
93
|
+
setCollapsedGroups((current) => {
|
|
94
|
+
const next = new Map(current);
|
|
95
|
+
next.set(
|
|
96
|
+
group.id,
|
|
97
|
+
!getGroupCollapsed(current, group)
|
|
98
|
+
);
|
|
99
|
+
return next;
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
103
|
+
"div",
|
|
104
|
+
{
|
|
105
|
+
className: classes,
|
|
106
|
+
...props,
|
|
107
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "course-outline__groups", children: visibleGroups.map((group, groupIndex) => {
|
|
108
|
+
const isCollapsed = collapsible && getGroupCollapsed(
|
|
109
|
+
collapsedGroups,
|
|
110
|
+
group
|
|
111
|
+
);
|
|
112
|
+
const groupContentId = `${componentId}-group-${groupIndex}`;
|
|
113
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
114
|
+
"li",
|
|
115
|
+
{
|
|
116
|
+
className: "course-outline__group",
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsxRuntime.jsx("header", { className: "course-outline__group-header", children: /* @__PURE__ */ jsxRuntime.jsx(GroupTitle, { level: headingLevel, children: collapsible ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
119
|
+
"button",
|
|
120
|
+
{
|
|
121
|
+
className: "course-outline__group-toggle",
|
|
122
|
+
type: "button",
|
|
123
|
+
"aria-expanded": !isCollapsed,
|
|
124
|
+
"aria-controls": groupContentId,
|
|
125
|
+
onClick: () => toggleGroup(group),
|
|
126
|
+
children: group.title
|
|
127
|
+
}
|
|
128
|
+
) : group.title }) }),
|
|
129
|
+
hasContent(group.description) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "course-outline__group-description", children: group.description }),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
131
|
+
"ol",
|
|
132
|
+
{
|
|
133
|
+
className: "course-outline__items",
|
|
134
|
+
id: groupContentId,
|
|
135
|
+
hidden: isCollapsed,
|
|
136
|
+
children: group.items.map((item) => {
|
|
137
|
+
const hasMeta = hasContent(item.meta);
|
|
138
|
+
const hasStatus = hasContent(
|
|
139
|
+
item.status
|
|
140
|
+
);
|
|
141
|
+
const hasAction = hasContent(
|
|
142
|
+
item.action
|
|
143
|
+
);
|
|
144
|
+
const hasFooter = hasStatus || hasAction;
|
|
145
|
+
const itemClasses = classnames.classnames(
|
|
146
|
+
"course-outline__item",
|
|
147
|
+
"",
|
|
148
|
+
{
|
|
149
|
+
"course-outline__item--active": Boolean(item.active),
|
|
150
|
+
"course-outline__item--disabled": Boolean(item.disabled)
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
154
|
+
"li",
|
|
155
|
+
{
|
|
156
|
+
className: itemClasses,
|
|
157
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
158
|
+
"article",
|
|
159
|
+
{
|
|
160
|
+
className: "course-outline__item-body",
|
|
161
|
+
"aria-disabled": item.disabled ? "true" : void 0,
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "course-outline__title", children: item.title }),
|
|
164
|
+
hasMeta && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "course-outline__meta", children: item.meta }),
|
|
165
|
+
hasFooter && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "course-outline__footer", children: [
|
|
166
|
+
hasStatus && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "course-outline__status", children: item.status }),
|
|
167
|
+
hasAction && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "course-outline__action", children: item.action })
|
|
168
|
+
] })
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
},
|
|
173
|
+
item.id
|
|
174
|
+
);
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
group.id
|
|
181
|
+
);
|
|
182
|
+
}) })
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
var course_outline_default = CourseOutline;
|
|
187
|
+
|
|
188
|
+
module.exports = course_outline_default;
|
|
189
|
+
//# sourceMappingURL=course-outline.cjs.map
|
|
190
|
+
//# sourceMappingURL=course-outline.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/course-outline/course-outline.tsx"],"names":["jsx","React","classnames","jsxs"],"mappings":"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","file":"course-outline.cjs","sourcesContent":["import React from 'react';\n\nimport './course-outline.css';\nimport { classnames } from '../helpers/classnames';\n\nexport type CourseOutlineHeadingLevel =\n | 2\n | 3\n | 4\n | 5\n | 6;\n\nexport interface CourseOutlineItem {\n id: React.Key;\n title: React.ReactNode;\n meta?: React.ReactNode;\n status?: React.ReactNode;\n action?: React.ReactNode;\n active?: boolean;\n disabled?: boolean;\n}\n\nexport interface CourseOutlineGroup {\n id: React.Key;\n title: React.ReactNode;\n description?: React.ReactNode;\n items: CourseOutlineItem[];\n defaultCollapsed?: boolean;\n}\n\nexport interface CourseOutlineProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'children'\n > {\n groups: CourseOutlineGroup[];\n headingLevel?: CourseOutlineHeadingLevel;\n collapsible?: boolean;\n}\n\ntype CollapsedGroups = Map<React.Key, boolean>;\n\nfunction hasContent(\n value: React.ReactNode,\n): value is Exclude<\n React.ReactNode,\n null | undefined\n> {\n return value !== null && value !== undefined;\n}\n\nfunction getInitialCollapsedGroups(\n groups: CourseOutlineGroup[],\n): CollapsedGroups {\n return new Map(\n groups.map((group) => [\n group.id,\n Boolean(group.defaultCollapsed),\n ]),\n );\n}\n\nfunction getGroupCollapsed(\n collapsedGroups: CollapsedGroups,\n group: CourseOutlineGroup,\n) {\n return (\n collapsedGroups.get(group.id) ??\n Boolean(group.defaultCollapsed)\n );\n}\n\ninterface GroupTitleProps {\n level?: CourseOutlineHeadingLevel;\n children: React.ReactNode;\n}\n\nconst GroupTitle: React.FC<GroupTitleProps> = ({\n level,\n children,\n}) => {\n if (!level) {\n return (\n <div className=\"course-outline__group-title\">\n {children}\n </div>\n );\n }\n\n switch (level) {\n case 2:\n return (\n <h2 className=\"course-outline__group-title\">\n {children}\n </h2>\n );\n\n case 3:\n return (\n <h3 className=\"course-outline__group-title\">\n {children}\n </h3>\n );\n\n case 4:\n return (\n <h4 className=\"course-outline__group-title\">\n {children}\n </h4>\n );\n\n case 5:\n return (\n <h5 className=\"course-outline__group-title\">\n {children}\n </h5>\n );\n\n default:\n return (\n <h6 className=\"course-outline__group-title\">\n {children}\n </h6>\n );\n }\n};\n\nconst CourseOutline: React.FC<CourseOutlineProps> = ({\n groups,\n headingLevel,\n collapsible = false,\n className = '',\n ...props\n}) => {\n const visibleGroups = React.useMemo(\n () =>\n groups.filter(\n (group) => group.items.length > 0,\n ),\n [groups],\n );\n\n const [collapsedGroups, setCollapsedGroups] =\n React.useState<CollapsedGroups>(() =>\n getInitialCollapsedGroups(visibleGroups),\n );\n\n const componentId = React.useId();\n\n React.useEffect(() => {\n if (!collapsible) {\n return;\n }\n\n setCollapsedGroups((current) => {\n const next = new Map(current);\n let changed = false;\n\n for (const group of visibleGroups) {\n if (!next.has(group.id)) {\n next.set(\n group.id,\n Boolean(group.defaultCollapsed),\n );\n\n changed = true;\n }\n }\n\n return changed ? next : current;\n });\n }, [collapsible, visibleGroups]);\n\n if (visibleGroups.length === 0) {\n return null;\n }\n\n const classes = classnames(\n 'course-outline',\n className,\n {\n 'course-outline--collapsible': collapsible,\n },\n );\n\n const toggleGroup = (\n group: CourseOutlineGroup,\n ) => {\n setCollapsedGroups((current) => {\n const next = new Map(current);\n\n next.set(\n group.id,\n !getGroupCollapsed(current, group),\n );\n\n return next;\n });\n };\n\n return (\n <div\n className={classes}\n {...props}\n >\n <ol className=\"course-outline__groups\">\n {visibleGroups.map((group, groupIndex) => {\n const isCollapsed =\n collapsible &&\n getGroupCollapsed(\n collapsedGroups,\n group,\n );\n\n const groupContentId =\n `${componentId}-group-${groupIndex}`;\n\n return (\n <li\n key={group.id}\n className=\"course-outline__group\"\n >\n <header className=\"course-outline__group-header\">\n <GroupTitle level={headingLevel}>\n {collapsible ? (\n <button\n className=\"course-outline__group-toggle\"\n type=\"button\"\n aria-expanded={!isCollapsed}\n aria-controls={groupContentId}\n onClick={() => toggleGroup(group)}\n >\n {group.title}\n </button>\n ) : (\n group.title\n )}\n </GroupTitle>\n </header>\n\n {hasContent(group.description) && (\n <div className=\"course-outline__group-description\">\n {group.description}\n </div>\n )}\n\n <ol\n className=\"course-outline__items\"\n id={groupContentId}\n hidden={isCollapsed}\n >\n {group.items.map((item) => {\n const hasMeta = hasContent(item.meta);\n const hasStatus = hasContent(\n item.status,\n );\n const hasAction = hasContent(\n item.action,\n );\n\n const hasFooter =\n hasStatus || hasAction;\n\n const itemClasses = classnames(\n 'course-outline__item',\n '',\n {\n 'course-outline__item--active':\n Boolean(item.active),\n\n 'course-outline__item--disabled':\n Boolean(item.disabled),\n },\n );\n\n return (\n <li\n key={item.id}\n className={itemClasses}\n >\n <article\n className=\"course-outline__item-body\"\n aria-disabled={\n item.disabled\n ? 'true'\n : undefined\n }\n >\n <div className=\"course-outline__title\">\n {item.title}\n </div>\n\n {hasMeta && (\n <div className=\"course-outline__meta\">\n {item.meta}\n </div>\n )}\n\n {hasFooter && (\n <div className=\"course-outline__footer\">\n {hasStatus && (\n <div className=\"course-outline__status\">\n {item.status}\n </div>\n )}\n\n {hasAction && (\n <div className=\"course-outline__action\">\n {item.action}\n </div>\n )}\n </div>\n )}\n </article>\n </li>\n );\n })}\n </ol>\n </li>\n );\n })}\n </ol>\n </div>\n );\n};\n\nexport default CourseOutline;\n"]}
|