@xyd-js/framework 0.1.0-xyd.12 → 0.1.0-xyd.13

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.
Files changed (43) hide show
  1. package/package.json +8 -21
  2. package/packages/hydration/README.md +3 -0
  3. package/packages/hydration/index.ts +3 -0
  4. package/packages/hydration/settings-to-props.ts +325 -0
  5. package/packages/react/README.md +3 -0
  6. package/packages/react/components/index.tsx +337 -0
  7. package/packages/react/components/sidebar/index.ts +3 -0
  8. package/packages/react/components/sidebar/sidebar-group.tsx +240 -0
  9. package/packages/react/components/sidebar/sidebar.tsx +127 -0
  10. package/packages/react/contexts/framework.tsx +78 -0
  11. package/packages/react/contexts/index.ts +2 -0
  12. package/packages/react/contexts/ui.tsx +6 -0
  13. package/packages/react/hooks/index.ts +3 -0
  14. package/packages/react/hooks/useMatchedNav.tsx +29 -0
  15. package/packages/react/index.ts +12 -0
  16. package/packages/react/utils/manualHydration.ts +25 -0
  17. package/packages/theme/context.tsx +19 -0
  18. package/packages/theme/index.ts +0 -0
  19. package/postcss.config.cjs +5 -0
  20. package/src/index.ts +1 -0
  21. package/src/types.ts +7 -0
  22. package/tsconfig.json +45 -0
  23. package/tsup.config.ts +28 -0
  24. package/dist/hydration.d.mts +0 -11
  25. package/dist/hydration.d.ts +0 -11
  26. package/dist/hydration.js +0 -271
  27. package/dist/hydration.js.map +0 -1
  28. package/dist/hydration.mjs +0 -245
  29. package/dist/hydration.mjs.map +0 -1
  30. package/dist/index.d.mts +0 -8
  31. package/dist/index.d.ts +0 -8
  32. package/dist/index.js +0 -18
  33. package/dist/index.js.map +0 -1
  34. package/dist/index.mjs +0 -1
  35. package/dist/index.mjs.map +0 -1
  36. package/dist/react.d.mts +0 -32
  37. package/dist/react.d.ts +0 -32
  38. package/dist/react.js +0 -694
  39. package/dist/react.js.map +0 -1
  40. package/dist/react.mjs +0 -651
  41. package/dist/react.mjs.map +0 -1
  42. package/dist/sidebar-Dwf54qYp.d.mts +0 -15
  43. package/dist/sidebar-Dwf54qYp.d.ts +0 -15
package/package.json CHANGED
@@ -1,40 +1,27 @@
1
1
  {
2
2
  "name": "@xyd-js/framework",
3
- "version": "0.1.0-xyd.12",
3
+ "version": "0.1.0-xyd.13",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
- "module": "dist/index.mjs",
6
+ "type": "module",
7
7
  "types": "dist/index.d.ts",
8
8
  "exports": {
9
9
  "./package.json": "./package.json",
10
10
  "./index.css": "./dist/index.css",
11
- ".": {
12
- "require": "./dist/index.js",
13
- "import": "./dist/index.mjs"
14
- },
15
- "./hydration": {
16
- "require": "./dist/hydration.js",
17
- "import": "./dist/hydration.mjs"
18
- },
19
- "./react": {
20
- "require": "./dist/react.js",
21
- "import": "./dist/react.mjs"
22
- }
11
+ ".": "./dist/index.js",
12
+ "./hydration": "./dist/hydration.js",
13
+ "./react": "./dist/react.js"
23
14
  },
24
- "files": [
25
- "dist",
26
- "package.json"
27
- ],
28
15
  "author": "",
29
16
  "dependencies": {},
30
17
  "peerDependencies": {
31
18
  "react": "^18.3.1",
32
19
  "react-dom": "^18.3.1",
33
20
  "react-router": "^7.1.1",
34
- "@xyd-js/core": "0.1.0-xyd.2",
35
- "@xyd-js/components": "0.1.0-xyd.1",
36
21
  "@xyd-js/ui": "0.1.0-xyd.4",
37
- "@xyd-js/content": "0.1.0-xyd.4"
22
+ "@xyd-js/content": "0.1.0-xyd.4",
23
+ "@xyd-js/components": "0.1.0-xyd.1",
24
+ "@xyd-js/core": "0.1.0-xyd.2"
38
25
  },
39
26
  "devDependencies": {
40
27
  "@babel/preset-react": "^7.24.7",
@@ -0,0 +1,3 @@
1
+ # @xyd-js/framework/hydration
2
+
3
+ Hydration is a process of converting xyd core structures like settings into @xyd-js/ui via server-side.
@@ -0,0 +1,3 @@
1
+ export {
2
+ mapSettingsToProps
3
+ } from "./settings-to-props"
@@ -0,0 +1,325 @@
1
+ // server-only
2
+
3
+ import {Sidebar, PageFrontMatter, Settings, SidebarMulti} from "@xyd-js/core";
4
+ import {filterNavigationByLevels, pageFrontMatters} from "@xyd-js/content/navigation";
5
+ import {IBreadcrumb, INavLinks} from "@xyd-js/ui";
6
+
7
+ import {FwSidebarGroupProps} from "../react";
8
+
9
+ // TODO: framework vs content responsibility
10
+
11
+ function filterNavigation(settings: Settings, slug: string): Sidebar[] {
12
+ const sidebarItems: Sidebar[] = []
13
+
14
+ let multiSidebarMatch: SidebarMulti | null = null
15
+
16
+ settings?.structure?.sidebar.filter(sidebar => {
17
+ if ("match" in sidebar) {
18
+ const sideMatch = normalizeHref(sidebar.match)
19
+ const normalizeSlug = normalizeHref(slug)
20
+
21
+ // TODO: startWith is not enough e.g `/docs/apps/buildISSUE` if `/docs/apps/build`
22
+ if (normalizeSlug.startsWith(sideMatch)) {
23
+ if (multiSidebarMatch) {
24
+ const findByMatchLvl = multiSidebarMatch.match.split("/").length
25
+ const urlMatchLvl = sideMatch.split("/").length
26
+
27
+ if (urlMatchLvl > findByMatchLvl) {
28
+ multiSidebarMatch = sidebar
29
+ }
30
+ } else {
31
+ multiSidebarMatch = sidebar
32
+ }
33
+ }
34
+
35
+ return
36
+ }
37
+
38
+ // TODO: better algorithm
39
+ const ok = filterNavigationByLevels(settings?.structure?.header || [], slug)(sidebar)
40
+
41
+ if (ok) {
42
+ sidebarItems.push(sidebar)
43
+ }
44
+ })
45
+
46
+ if (multiSidebarMatch != null) {
47
+ const side = multiSidebarMatch as SidebarMulti
48
+ sidebarItems.push(...side.items)
49
+ }
50
+
51
+ return sidebarItems
52
+ }
53
+
54
+ // TODO: rename this because it's no longer 'navigation' because it returns breadcrumbs, sidebar and nextlinks props
55
+ // TODO: breadcrumbs and frontmatters as content plugins?
56
+ // TODO: idea - calculate breadcrumbs and navlinks near server-side component?
57
+
58
+ function normalizeHrefCheck(first: string, second: string) {
59
+ if (first.startsWith("/")) {
60
+ first = first.slice(1)
61
+ }
62
+
63
+ if (second.startsWith("/")) {
64
+ second = second.slice(1)
65
+ }
66
+
67
+ return first === second
68
+ }
69
+
70
+ function normalizeHref(href: string) {
71
+ if (href.startsWith("/")) {
72
+ return href
73
+ }
74
+
75
+ return `/${href}`
76
+ }
77
+
78
+ function safePageLink(page: string): string {
79
+ return page?.startsWith("/") ? page : `/${page}`
80
+ }
81
+
82
+ // mapNavigationToProps maps @xyd-js/core navigation into @xyd-js/ui props
83
+ export async function mapSettingsToProps(
84
+ settings: Settings,
85
+ slug: string
86
+ ): Promise<{
87
+ groups: FwSidebarGroupProps[],
88
+ breadcrumbs: IBreadcrumb[]
89
+ navlinks?: INavLinks
90
+ }> {
91
+ const filteredNav = filterNavigation(settings, slug)
92
+ const frontmatters = await pageFrontMatters(filteredNav)
93
+
94
+ const slugFrontmatter = frontmatters[slug] || null
95
+ const breadcrumbs: IBreadcrumb[] = []
96
+ let navlinks: INavLinks | undefined = undefined
97
+
98
+ function mapItems(
99
+ page: string | Sidebar,
100
+ currentNav: Sidebar,
101
+ nav: Sidebar[]
102
+ ) {
103
+ if (typeof page !== "string") {
104
+ const items = page.pages?.map((p) => mapItems(p, page, nav))
105
+
106
+ if (items?.find(item => normalizeHrefCheck(item.href, slug))) {
107
+ breadcrumbs.unshift({
108
+ title: page.group || "",
109
+ href: "", // TODO:
110
+ })
111
+ }
112
+
113
+ return {
114
+ title: page.group,
115
+ href: "",
116
+ active: false,
117
+ items,
118
+ }
119
+ }
120
+
121
+ const matterTitle = frontmatters && frontmatters[page] && frontmatters[page].title
122
+
123
+ let title = ""
124
+
125
+ if (typeof matterTitle === "string") {
126
+ title = matterTitle
127
+ } else {
128
+ // @ts-ignore
129
+ title = matterTitle
130
+ }
131
+
132
+ if (!title) {
133
+ console.error("Title not found for page", page)
134
+ }
135
+
136
+ // TODO: better data structures - for example flat array of filtered nav
137
+ if (slugFrontmatter && (slugFrontmatter === frontmatters[page])) {
138
+ const nlinks = mapNavToLinks(page, currentNav, nav, frontmatters)
139
+
140
+ if (nlinks) {
141
+ navlinks = nlinks
142
+ }
143
+
144
+ if (currentNav.group) {
145
+ breadcrumbs.push({
146
+ title: currentNav.group,
147
+ href: "", // TODO:
148
+ })
149
+ }
150
+ breadcrumbs.push({
151
+ // @ts-ignore
152
+ title,
153
+ href: page,
154
+ })
155
+ }
156
+
157
+ return {
158
+ // @ts-ignore
159
+ title,
160
+ href: safePageLink(page),
161
+ active: false // TODO:
162
+ }
163
+ }
164
+
165
+ const groups = filteredNav.map((nav) => {
166
+ // TODO: finish
167
+ if ("match" in nav) {
168
+ return {
169
+ group: "",
170
+ items: [],
171
+ } as FwSidebarGroupProps
172
+ }
173
+
174
+ return {
175
+ group: nav.group,
176
+ items: nav.pages?.map((p) => {
177
+ // @ts-ignore
178
+ return mapItems(p, nav, filteredNav)
179
+ }) || [],
180
+ } as FwSidebarGroupProps
181
+ }) || []
182
+
183
+ return {
184
+ groups,
185
+ breadcrumbs,
186
+ navlinks
187
+ }
188
+ }
189
+
190
+ function mapNavToLinks(
191
+ page: string | Sidebar,
192
+ currentNav: Sidebar,
193
+ nav: Sidebar[],
194
+ frontmatters: PageFrontMatter
195
+ ): INavLinks | undefined {
196
+ if (!currentNav.group) {
197
+ console.error("current nav need group to calculate navlinks")
198
+ return
199
+ }
200
+
201
+ const currentPageIndex = currentNav?.pages?.findIndex(p => page === p)
202
+ const foundPageIndex = currentPageIndex != undefined && currentPageIndex !== -1
203
+
204
+ if (!foundPageIndex) {
205
+ return
206
+ }
207
+
208
+ let prev = currentNav?.pages?.[currentPageIndex - 1]
209
+ let next = currentNav?.pages?.[currentPageIndex + 1]
210
+
211
+ if (prev && next) {
212
+ if (typeof prev !== "string" || typeof next !== "string") {
213
+ console.error("currently nested pages for navlinks are not supported (step 1)")
214
+ return
215
+ }
216
+
217
+ const prevTitle = frontmatters[prev]?.title || ""
218
+ const nextTitle = frontmatters[next]?.title || ""
219
+
220
+ // prev and next found on same nav (pages)
221
+ return {
222
+ prev: {
223
+ // @ts-ignore
224
+ title: prevTitle,
225
+ href: safePageLink(prev),
226
+ },
227
+ next: {
228
+ // @ts-ignore
229
+ title: nextTitle,
230
+ href: safePageLink(next),
231
+ }
232
+ }
233
+ }
234
+
235
+ const currentNavPosition = nav.findIndex(n => n.group === currentNav.group)
236
+ const foundNavIndex = currentNavPosition != undefined && currentNavPosition !== -1
237
+
238
+ if (!foundNavIndex) {
239
+ return
240
+ }
241
+
242
+ // search prev in previous nav
243
+ if (!prev) {
244
+ const prevNav = nav[currentNavPosition - 1]
245
+
246
+ if (prevNav) {
247
+ const prevNavLastPge = prevNav?.pages?.[prevNav.pages.length - 1]
248
+
249
+ if (typeof prevNavLastPge != "string") {
250
+ console.error("currently nested pages for navlinks are not supported (step 2)")
251
+ } else {
252
+ prev = prevNavLastPge
253
+ }
254
+ }
255
+ }
256
+
257
+ // search next in next nav
258
+ if (!next) {
259
+ const nextNav = nav[currentNavPosition + 1]
260
+
261
+ if (nextNav) {
262
+ const nextNavFirstPage = nextNav?.pages?.[0]
263
+
264
+ if (typeof nextNavFirstPage != "string") {
265
+ console.error("currently nested pages for navlinks are not supported (step 3)")
266
+ } else {
267
+ next = nextNavFirstPage
268
+ }
269
+ }
270
+ }
271
+
272
+
273
+ if (!prev) {
274
+ if (typeof next == "string") {
275
+ const nextTitle = frontmatters[next]?.title || ""
276
+
277
+ return {
278
+ next: {
279
+ // @ts-ignore
280
+ title: nextTitle,
281
+ href: safePageLink(next),
282
+ },
283
+ }
284
+ } else {
285
+ console.error("currently nested pages for navlinks are not supported (step 4)")
286
+ }
287
+ }
288
+
289
+ if (!next) {
290
+ if (typeof prev == "string") {
291
+ const nextTitle = frontmatters[prev]?.title || ""
292
+
293
+ return {
294
+ prev: {
295
+ // @ts-ignore
296
+ title: nextTitle,
297
+ href: safePageLink(prev),
298
+ },
299
+ }
300
+ } else {
301
+ console.error("currently nested pages for navlinks are not supported (step 5)")
302
+ }
303
+ }
304
+
305
+ if (typeof prev !== "string" || typeof next !== "string") {
306
+ console.error("currently nested pages for navlinks are not supported (step 6)")
307
+ return
308
+ }
309
+
310
+ const prevTitle = frontmatters[prev]?.title || ""
311
+ const nextTitle = frontmatters[next]?.title || ""
312
+
313
+ return {
314
+ prev: {
315
+ // @ts-ignore
316
+ title: prevTitle,
317
+ href: safePageLink(prev),
318
+ },
319
+ next: {
320
+ // @ts-ignore
321
+ title: nextTitle,
322
+ href: safePageLink(next),
323
+ }
324
+ }
325
+ }
@@ -0,0 +1,3 @@
1
+ # @xyd-js/framework/react
2
+
3
+ React components for xyd framework.