@rimelight/ui 0.0.2 → 0.0.3

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 (103) hide show
  1. package/package.json +5 -1
  2. package/src/components/carousel/carousel-script.ts +188 -188
  3. package/src/components/carousel/index.ts +1 -1
  4. package/src/components/input-otp/InputOtpTypes.ts +6 -6
  5. package/src/components/radio-group/RadioGroupTypes.ts +6 -6
  6. package/src/components/select/SelectTypes.ts +13 -13
  7. package/src/components/switch/SwitchTypes.ts +6 -6
  8. package/src/components/toast/index.ts +1 -1
  9. package/src/components/toast/toast-manager.ts +234 -234
  10. package/src/components/toggle/ToggleTypes.ts +14 -14
  11. package/src/config/cookies.ts +5 -5
  12. package/src/config/index.ts +3 -3
  13. package/src/config/links.ts +4 -4
  14. package/src/config/site.config.ts +45 -45
  15. package/src/domain/cms/index.ts +1 -1
  16. package/src/domain/cms/types.ts +20 -20
  17. package/src/domain/i18n/constants.ts +21 -21
  18. package/src/domain/i18n/cookie-storage.ts +13 -13
  19. package/src/domain/i18n/country-to-locale-map.ts +67 -67
  20. package/src/domain/i18n/fetcher.ts +268 -268
  21. package/src/domain/i18n/flags.ts +16 -16
  22. package/src/domain/i18n/format.ts +90 -90
  23. package/src/domain/i18n/index.ts +10 -10
  24. package/src/domain/i18n/middleware/i18n.ts +160 -160
  25. package/src/domain/i18n/resolve-locale.ts +55 -55
  26. package/src/domain/i18n/schema.ts +29 -29
  27. package/src/domain/index.ts +4 -4
  28. package/src/domain/seo/constants.ts +20 -20
  29. package/src/domain/seo/index.ts +3 -3
  30. package/src/domain/seo/services/index.ts +3 -3
  31. package/src/domain/seo/services/llms.ts +53 -53
  32. package/src/domain/seo/services/robots.ts +12 -12
  33. package/src/domain/seo/services/sitemap.ts +56 -56
  34. package/src/domain/seo/utils.ts +37 -37
  35. package/src/domain/theme/constants.ts +6 -6
  36. package/src/domain/theme/index.ts +2 -2
  37. package/src/domain/theme/schema.ts +10 -10
  38. package/src/env.d.ts +49 -49
  39. package/src/integrations/index.ts +1 -0
  40. package/src/integrations/sri.ts +92 -0
  41. package/src/lib/component-preview.ts +73 -73
  42. package/src/lib/index.ts +6 -6
  43. package/src/lib/remark/remark-modified-time.ts +13 -13
  44. package/src/lib/showcase-preview-elements.ts +408 -408
  45. package/src/lib/showcase-renderer.ts +218 -218
  46. package/src/lib/showcase-utils.ts +189 -189
  47. package/src/lib/turnstile.ts +35 -35
  48. package/src/lib/utils/starwind/positioning.ts +318 -318
  49. package/src/locales/de/blog.json +9 -9
  50. package/src/locales/de/common.json +19 -19
  51. package/src/locales/de/errors.json +6 -6
  52. package/src/locales/de/home.json +20 -20
  53. package/src/locales/de/messages.json +8 -8
  54. package/src/locales/de/nav.json +13 -13
  55. package/src/locales/de/projects.json +9 -9
  56. package/src/locales/de/services.json +43 -43
  57. package/src/locales/en/blog.json +9 -9
  58. package/src/locales/en/chat.json +29 -29
  59. package/src/locales/en/common.json +19 -19
  60. package/src/locales/en/cookie_notice.json +11 -11
  61. package/src/locales/en/editor.json +24 -24
  62. package/src/locales/en/errors.json +6 -6
  63. package/src/locales/en/home.json +20 -20
  64. package/src/locales/en/messages.json +8 -8
  65. package/src/locales/en/nav.json +13 -13
  66. package/src/locales/en/projects.json +9 -9
  67. package/src/locales/en/services.json +43 -43
  68. package/src/locales/es/blog.json +9 -9
  69. package/src/locales/es/common.json +19 -19
  70. package/src/locales/es/errors.json +6 -6
  71. package/src/locales/es/home.json +20 -20
  72. package/src/locales/es/messages.json +8 -8
  73. package/src/locales/es/nav.json +13 -13
  74. package/src/locales/es/projects.json +9 -9
  75. package/src/locales/es/services.json +43 -43
  76. package/src/locales/ja/blog.json +9 -9
  77. package/src/locales/ja/common.json +19 -19
  78. package/src/locales/ja/errors.json +6 -6
  79. package/src/locales/ja/home.json +20 -20
  80. package/src/locales/ja/messages.json +8 -8
  81. package/src/locales/ja/nav.json +13 -13
  82. package/src/locales/ja/projects.json +9 -9
  83. package/src/locales/ja/services.json +43 -43
  84. package/src/locales/pt/blog.json +9 -9
  85. package/src/locales/pt/chat.json +29 -29
  86. package/src/locales/pt/common.json +19 -19
  87. package/src/locales/pt/cookie_notice.json +11 -11
  88. package/src/locales/pt/editor.json +24 -24
  89. package/src/locales/pt/errors.json +6 -6
  90. package/src/locales/pt/home.json +20 -20
  91. package/src/locales/pt/messages.json +8 -8
  92. package/src/locales/pt/nav.json +13 -13
  93. package/src/locales/pt/projects.json +9 -9
  94. package/src/locales/pt/services.json +43 -43
  95. package/src/middleware/index.ts +1 -0
  96. package/src/middleware/sri.ts +76 -0
  97. package/src/utils/index.ts +2 -2
  98. package/src/utils/server/cookies.ts +98 -98
  99. package/src/utils/server/index.ts +1 -1
  100. package/src/utils/shared/deep-merge.ts +36 -36
  101. package/src/utils/shared/embed.ts +105 -105
  102. package/src/utils/shared/index.ts +3 -3
  103. package/src/utils/shared/parse-boolean.ts +14 -14
@@ -1,318 +1,318 @@
1
- export type FloatingSide = "top" | "bottom" | "left" | "right"
2
- export type FloatingAlign = "start" | "center" | "end"
3
-
4
- interface Placement {
5
- side: FloatingSide
6
- align: FloatingAlign
7
- }
8
-
9
- interface Position {
10
- top: number
11
- left: number
12
- }
13
-
14
- interface OverflowAmount {
15
- left: number
16
- right: number
17
- top: number
18
- bottom: number
19
- }
20
-
21
- export interface ResolvePlacementOptions {
22
- side: FloatingSide
23
- align: FloatingAlign
24
- sideOffset: number
25
- triggerRect: DOMRect
26
- contentWidth: number
27
- contentHeight: number
28
- viewportWidth: number
29
- viewportHeight: number
30
- viewportPadding: number
31
- avoidCollisions?: boolean
32
- }
33
-
34
- export interface ResolvePlacementResult {
35
- side: FloatingSide
36
- align: FloatingAlign
37
- top: number
38
- left: number
39
- }
40
-
41
- function getOppositeSide(side: FloatingSide): FloatingSide {
42
- switch (side) {
43
- case "top":
44
- return "bottom"
45
- case "bottom":
46
- return "top"
47
- case "left":
48
- return "right"
49
- case "right":
50
- return "left"
51
- }
52
- }
53
-
54
- function getOppositeAlign(align: FloatingAlign): FloatingAlign {
55
- switch (align) {
56
- case "start":
57
- return "end"
58
- case "end":
59
- return "start"
60
- case "center":
61
- return "center"
62
- }
63
- }
64
-
65
- function getPlacementCandidates(side: FloatingSide, align: FloatingAlign): Placement[] {
66
- const placements: Placement[] = []
67
-
68
- const pushUnique = (candidate: Placement) => {
69
- if (
70
- !placements.some(
71
- (placement) => placement.side === candidate.side && placement.align === candidate.align
72
- )
73
- ) {
74
- placements.push(candidate)
75
- }
76
- }
77
-
78
- const oppositeSide = getOppositeSide(side)
79
- const oppositeAlign = getOppositeAlign(align)
80
-
81
- pushUnique({ side, align })
82
- pushUnique({ side: oppositeSide, align })
83
- pushUnique({ side, align: "center" })
84
- pushUnique({ side: oppositeSide, align: "center" })
85
- pushUnique({ side, align: oppositeAlign })
86
- pushUnique({ side: oppositeSide, align: oppositeAlign })
87
-
88
- if (side === "top" || side === "bottom") {
89
- for (const fallbackAlign of ["start", "center", "end"] as const) {
90
- pushUnique({ side: "right", align: fallbackAlign })
91
- pushUnique({ side: "left", align: fallbackAlign })
92
- }
93
- } else {
94
- for (const fallbackAlign of ["start", "center", "end"] as const) {
95
- pushUnique({ side: "bottom", align: fallbackAlign })
96
- pushUnique({ side: "top", align: fallbackAlign })
97
- }
98
- }
99
-
100
- return placements
101
- }
102
-
103
- function getPlacementPosition(
104
- placement: Placement,
105
- triggerRect: DOMRect,
106
- contentWidth: number,
107
- contentHeight: number,
108
- sideOffset: number
109
- ): Position {
110
- let top = 0
111
- let left = 0
112
-
113
- if (placement.side === "bottom" || placement.side === "top") {
114
- top =
115
- placement.side === "bottom"
116
- ? triggerRect.bottom + sideOffset
117
- : triggerRect.top - contentHeight - sideOffset
118
-
119
- if (placement.align === "start") {
120
- left = triggerRect.left
121
- } else if (placement.align === "end") {
122
- left = triggerRect.right - contentWidth
123
- } else {
124
- left = triggerRect.left + (triggerRect.width - contentWidth) / 2
125
- }
126
- } else {
127
- left =
128
- placement.side === "right"
129
- ? triggerRect.right + sideOffset
130
- : triggerRect.left - contentWidth - sideOffset
131
-
132
- if (placement.align === "start") {
133
- top = triggerRect.top
134
- } else if (placement.align === "end") {
135
- top = triggerRect.bottom - contentHeight
136
- } else {
137
- top = triggerRect.top + (triggerRect.height - contentHeight) / 2
138
- }
139
- }
140
-
141
- return { top, left }
142
- }
143
-
144
- function clampPositionToViewport(
145
- position: Position,
146
- contentWidth: number,
147
- contentHeight: number,
148
- viewportWidth: number,
149
- viewportHeight: number,
150
- viewportPadding: number
151
- ): Position {
152
- const maxLeft = Math.max(viewportPadding, viewportWidth - contentWidth - viewportPadding)
153
- const maxTop = Math.max(viewportPadding, viewportHeight - contentHeight - viewportPadding)
154
-
155
- return {
156
- left: Math.min(Math.max(viewportPadding, position.left), maxLeft),
157
- top: Math.min(Math.max(viewportPadding, position.top), maxTop)
158
- }
159
- }
160
-
161
- function getOverflowAmount(
162
- position: Position,
163
- contentWidth: number,
164
- contentHeight: number,
165
- viewportWidth: number,
166
- viewportHeight: number,
167
- viewportPadding: number
168
- ): OverflowAmount {
169
- const right = position.left + contentWidth
170
- const bottom = position.top + contentHeight
171
-
172
- return {
173
- left: Math.max(0, viewportPadding - position.left),
174
- top: Math.max(0, viewportPadding - position.top),
175
- right: Math.max(0, right - (viewportWidth - viewportPadding)),
176
- bottom: Math.max(0, bottom - (viewportHeight - viewportPadding))
177
- }
178
- }
179
-
180
- function getMainAxisShift(
181
- side: FloatingSide,
182
- preferredPosition: Position,
183
- clampedPosition: Position
184
- ): number {
185
- if (side === "top" || side === "bottom") {
186
- return Math.abs(clampedPosition.top - preferredPosition.top)
187
- }
188
-
189
- return Math.abs(clampedPosition.left - preferredPosition.left)
190
- }
191
-
192
- function getCrossAxisShift(
193
- side: FloatingSide,
194
- preferredPosition: Position,
195
- clampedPosition: Position
196
- ): number {
197
- if (side === "top" || side === "bottom") {
198
- return Math.abs(clampedPosition.left - preferredPosition.left)
199
- }
200
-
201
- return Math.abs(clampedPosition.top - preferredPosition.top)
202
- }
203
-
204
- /**
205
- * Resolves the best side/alignment placement for floating content.
206
- */
207
- export function resolvePlacement(options: ResolvePlacementOptions): ResolvePlacementResult {
208
- const {
209
- side,
210
- align,
211
- sideOffset,
212
- triggerRect,
213
- contentWidth,
214
- contentHeight,
215
- viewportWidth,
216
- viewportHeight,
217
- viewportPadding,
218
- avoidCollisions = true
219
- } = options
220
-
221
- const preferredPlacement: Placement = { side, align }
222
-
223
- if (!avoidCollisions) {
224
- const preferredPosition = getPlacementPosition(
225
- preferredPlacement,
226
- triggerRect,
227
- contentWidth,
228
- contentHeight,
229
- sideOffset
230
- )
231
-
232
- return {
233
- side,
234
- align,
235
- top: preferredPosition.top,
236
- left: preferredPosition.left
237
- }
238
- }
239
-
240
- const candidates = getPlacementCandidates(side, align)
241
-
242
- let bestPlacement = candidates[0] ?? preferredPlacement
243
- let bestPosition = clampPositionToViewport(
244
- getPlacementPosition(bestPlacement, triggerRect, contentWidth, contentHeight, sideOffset),
245
- contentWidth,
246
- contentHeight,
247
- viewportWidth,
248
- viewportHeight,
249
- viewportPadding
250
- )
251
- let bestScore = Number.POSITIVE_INFINITY
252
-
253
- for (const placement of candidates) {
254
- const preferredPosition = getPlacementPosition(
255
- placement,
256
- triggerRect,
257
- contentWidth,
258
- contentHeight,
259
- sideOffset
260
- )
261
- const clampedPosition = clampPositionToViewport(
262
- preferredPosition,
263
- contentWidth,
264
- contentHeight,
265
- viewportWidth,
266
- viewportHeight,
267
- viewportPadding
268
- )
269
-
270
- const overflow = getOverflowAmount(
271
- preferredPosition,
272
- contentWidth,
273
- contentHeight,
274
- viewportWidth,
275
- viewportHeight,
276
- viewportPadding
277
- )
278
-
279
- const overflowTotal = overflow.left + overflow.right + overflow.top + overflow.bottom
280
- const mainAxisShift = getMainAxisShift(placement.side, preferredPosition, clampedPosition)
281
- const crossAxisShift = getCrossAxisShift(placement.side, preferredPosition, clampedPosition)
282
-
283
- const sidePenalty = placement.side === side ? 0 : 32
284
- const alignPenalty = placement.align === align ? 0 : 8
285
- const score =
286
- overflowTotal * 24 + mainAxisShift * 8 + crossAxisShift * 3 + sidePenalty + alignPenalty
287
-
288
- if (score < bestScore) {
289
- bestScore = score
290
- bestPlacement = placement
291
- bestPosition = clampedPosition
292
- }
293
- }
294
-
295
- return {
296
- side: bestPlacement.side,
297
- align: bestPlacement.align,
298
- top: bestPosition.top,
299
- left: bestPosition.left
300
- }
301
- }
302
-
303
- /**
304
- * Returns a transform-origin value that matches the resolved placement.
305
- */
306
- export function getTransformOrigin(side: FloatingSide, align: FloatingAlign): string {
307
- if (side === "top" || side === "bottom") {
308
- const vertical = side === "bottom" ? "top" : "bottom"
309
- const horizontal = align === "start" ? "left" : align === "end" ? "right" : "center"
310
-
311
- return `${horizontal} ${vertical}`
312
- }
313
-
314
- const horizontal = side === "right" ? "left" : "right"
315
- const vertical = align === "start" ? "top" : align === "end" ? "bottom" : "center"
316
-
317
- return `${horizontal} ${vertical}`
318
- }
1
+ export type FloatingSide = "top" | "bottom" | "left" | "right"
2
+ export type FloatingAlign = "start" | "center" | "end"
3
+
4
+ interface Placement {
5
+ side: FloatingSide
6
+ align: FloatingAlign
7
+ }
8
+
9
+ interface Position {
10
+ top: number
11
+ left: number
12
+ }
13
+
14
+ interface OverflowAmount {
15
+ left: number
16
+ right: number
17
+ top: number
18
+ bottom: number
19
+ }
20
+
21
+ export interface ResolvePlacementOptions {
22
+ side: FloatingSide
23
+ align: FloatingAlign
24
+ sideOffset: number
25
+ triggerRect: DOMRect
26
+ contentWidth: number
27
+ contentHeight: number
28
+ viewportWidth: number
29
+ viewportHeight: number
30
+ viewportPadding: number
31
+ avoidCollisions?: boolean
32
+ }
33
+
34
+ export interface ResolvePlacementResult {
35
+ side: FloatingSide
36
+ align: FloatingAlign
37
+ top: number
38
+ left: number
39
+ }
40
+
41
+ function getOppositeSide(side: FloatingSide): FloatingSide {
42
+ switch (side) {
43
+ case "top":
44
+ return "bottom"
45
+ case "bottom":
46
+ return "top"
47
+ case "left":
48
+ return "right"
49
+ case "right":
50
+ return "left"
51
+ }
52
+ }
53
+
54
+ function getOppositeAlign(align: FloatingAlign): FloatingAlign {
55
+ switch (align) {
56
+ case "start":
57
+ return "end"
58
+ case "end":
59
+ return "start"
60
+ case "center":
61
+ return "center"
62
+ }
63
+ }
64
+
65
+ function getPlacementCandidates(side: FloatingSide, align: FloatingAlign): Placement[] {
66
+ const placements: Placement[] = []
67
+
68
+ const pushUnique = (candidate: Placement) => {
69
+ if (
70
+ !placements.some(
71
+ (placement) => placement.side === candidate.side && placement.align === candidate.align
72
+ )
73
+ ) {
74
+ placements.push(candidate)
75
+ }
76
+ }
77
+
78
+ const oppositeSide = getOppositeSide(side)
79
+ const oppositeAlign = getOppositeAlign(align)
80
+
81
+ pushUnique({ side, align })
82
+ pushUnique({ side: oppositeSide, align })
83
+ pushUnique({ side, align: "center" })
84
+ pushUnique({ side: oppositeSide, align: "center" })
85
+ pushUnique({ side, align: oppositeAlign })
86
+ pushUnique({ side: oppositeSide, align: oppositeAlign })
87
+
88
+ if (side === "top" || side === "bottom") {
89
+ for (const fallbackAlign of ["start", "center", "end"] as const) {
90
+ pushUnique({ side: "right", align: fallbackAlign })
91
+ pushUnique({ side: "left", align: fallbackAlign })
92
+ }
93
+ } else {
94
+ for (const fallbackAlign of ["start", "center", "end"] as const) {
95
+ pushUnique({ side: "bottom", align: fallbackAlign })
96
+ pushUnique({ side: "top", align: fallbackAlign })
97
+ }
98
+ }
99
+
100
+ return placements
101
+ }
102
+
103
+ function getPlacementPosition(
104
+ placement: Placement,
105
+ triggerRect: DOMRect,
106
+ contentWidth: number,
107
+ contentHeight: number,
108
+ sideOffset: number
109
+ ): Position {
110
+ let top = 0
111
+ let left = 0
112
+
113
+ if (placement.side === "bottom" || placement.side === "top") {
114
+ top =
115
+ placement.side === "bottom"
116
+ ? triggerRect.bottom + sideOffset
117
+ : triggerRect.top - contentHeight - sideOffset
118
+
119
+ if (placement.align === "start") {
120
+ left = triggerRect.left
121
+ } else if (placement.align === "end") {
122
+ left = triggerRect.right - contentWidth
123
+ } else {
124
+ left = triggerRect.left + (triggerRect.width - contentWidth) / 2
125
+ }
126
+ } else {
127
+ left =
128
+ placement.side === "right"
129
+ ? triggerRect.right + sideOffset
130
+ : triggerRect.left - contentWidth - sideOffset
131
+
132
+ if (placement.align === "start") {
133
+ top = triggerRect.top
134
+ } else if (placement.align === "end") {
135
+ top = triggerRect.bottom - contentHeight
136
+ } else {
137
+ top = triggerRect.top + (triggerRect.height - contentHeight) / 2
138
+ }
139
+ }
140
+
141
+ return { top, left }
142
+ }
143
+
144
+ function clampPositionToViewport(
145
+ position: Position,
146
+ contentWidth: number,
147
+ contentHeight: number,
148
+ viewportWidth: number,
149
+ viewportHeight: number,
150
+ viewportPadding: number
151
+ ): Position {
152
+ const maxLeft = Math.max(viewportPadding, viewportWidth - contentWidth - viewportPadding)
153
+ const maxTop = Math.max(viewportPadding, viewportHeight - contentHeight - viewportPadding)
154
+
155
+ return {
156
+ left: Math.min(Math.max(viewportPadding, position.left), maxLeft),
157
+ top: Math.min(Math.max(viewportPadding, position.top), maxTop)
158
+ }
159
+ }
160
+
161
+ function getOverflowAmount(
162
+ position: Position,
163
+ contentWidth: number,
164
+ contentHeight: number,
165
+ viewportWidth: number,
166
+ viewportHeight: number,
167
+ viewportPadding: number
168
+ ): OverflowAmount {
169
+ const right = position.left + contentWidth
170
+ const bottom = position.top + contentHeight
171
+
172
+ return {
173
+ left: Math.max(0, viewportPadding - position.left),
174
+ top: Math.max(0, viewportPadding - position.top),
175
+ right: Math.max(0, right - (viewportWidth - viewportPadding)),
176
+ bottom: Math.max(0, bottom - (viewportHeight - viewportPadding))
177
+ }
178
+ }
179
+
180
+ function getMainAxisShift(
181
+ side: FloatingSide,
182
+ preferredPosition: Position,
183
+ clampedPosition: Position
184
+ ): number {
185
+ if (side === "top" || side === "bottom") {
186
+ return Math.abs(clampedPosition.top - preferredPosition.top)
187
+ }
188
+
189
+ return Math.abs(clampedPosition.left - preferredPosition.left)
190
+ }
191
+
192
+ function getCrossAxisShift(
193
+ side: FloatingSide,
194
+ preferredPosition: Position,
195
+ clampedPosition: Position
196
+ ): number {
197
+ if (side === "top" || side === "bottom") {
198
+ return Math.abs(clampedPosition.left - preferredPosition.left)
199
+ }
200
+
201
+ return Math.abs(clampedPosition.top - preferredPosition.top)
202
+ }
203
+
204
+ /**
205
+ * Resolves the best side/alignment placement for floating content.
206
+ */
207
+ export function resolvePlacement(options: ResolvePlacementOptions): ResolvePlacementResult {
208
+ const {
209
+ side,
210
+ align,
211
+ sideOffset,
212
+ triggerRect,
213
+ contentWidth,
214
+ contentHeight,
215
+ viewportWidth,
216
+ viewportHeight,
217
+ viewportPadding,
218
+ avoidCollisions = true
219
+ } = options
220
+
221
+ const preferredPlacement: Placement = { side, align }
222
+
223
+ if (!avoidCollisions) {
224
+ const preferredPosition = getPlacementPosition(
225
+ preferredPlacement,
226
+ triggerRect,
227
+ contentWidth,
228
+ contentHeight,
229
+ sideOffset
230
+ )
231
+
232
+ return {
233
+ side,
234
+ align,
235
+ top: preferredPosition.top,
236
+ left: preferredPosition.left
237
+ }
238
+ }
239
+
240
+ const candidates = getPlacementCandidates(side, align)
241
+
242
+ let bestPlacement = candidates[0] ?? preferredPlacement
243
+ let bestPosition = clampPositionToViewport(
244
+ getPlacementPosition(bestPlacement, triggerRect, contentWidth, contentHeight, sideOffset),
245
+ contentWidth,
246
+ contentHeight,
247
+ viewportWidth,
248
+ viewportHeight,
249
+ viewportPadding
250
+ )
251
+ let bestScore = Number.POSITIVE_INFINITY
252
+
253
+ for (const placement of candidates) {
254
+ const preferredPosition = getPlacementPosition(
255
+ placement,
256
+ triggerRect,
257
+ contentWidth,
258
+ contentHeight,
259
+ sideOffset
260
+ )
261
+ const clampedPosition = clampPositionToViewport(
262
+ preferredPosition,
263
+ contentWidth,
264
+ contentHeight,
265
+ viewportWidth,
266
+ viewportHeight,
267
+ viewportPadding
268
+ )
269
+
270
+ const overflow = getOverflowAmount(
271
+ preferredPosition,
272
+ contentWidth,
273
+ contentHeight,
274
+ viewportWidth,
275
+ viewportHeight,
276
+ viewportPadding
277
+ )
278
+
279
+ const overflowTotal = overflow.left + overflow.right + overflow.top + overflow.bottom
280
+ const mainAxisShift = getMainAxisShift(placement.side, preferredPosition, clampedPosition)
281
+ const crossAxisShift = getCrossAxisShift(placement.side, preferredPosition, clampedPosition)
282
+
283
+ const sidePenalty = placement.side === side ? 0 : 32
284
+ const alignPenalty = placement.align === align ? 0 : 8
285
+ const score =
286
+ overflowTotal * 24 + mainAxisShift * 8 + crossAxisShift * 3 + sidePenalty + alignPenalty
287
+
288
+ if (score < bestScore) {
289
+ bestScore = score
290
+ bestPlacement = placement
291
+ bestPosition = clampedPosition
292
+ }
293
+ }
294
+
295
+ return {
296
+ side: bestPlacement.side,
297
+ align: bestPlacement.align,
298
+ top: bestPosition.top,
299
+ left: bestPosition.left
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Returns a transform-origin value that matches the resolved placement.
305
+ */
306
+ export function getTransformOrigin(side: FloatingSide, align: FloatingAlign): string {
307
+ if (side === "top" || side === "bottom") {
308
+ const vertical = side === "bottom" ? "top" : "bottom"
309
+ const horizontal = align === "start" ? "left" : align === "end" ? "right" : "center"
310
+
311
+ return `${horizontal} ${vertical}`
312
+ }
313
+
314
+ const horizontal = side === "right" ? "left" : "right"
315
+ const vertical = align === "start" ? "top" : align === "end" ? "bottom" : "center"
316
+
317
+ return `${horizontal} ${vertical}`
318
+ }
@@ -1,9 +1,9 @@
1
- {
2
- "meta_title": "Blog | Daniel Marchi",
3
- "hero_title": "Blog",
4
- "hero_description": "Meine Einblicke auf meiner Reise als Entwickler.",
5
- "latest_article": "Neuester Artikel",
6
- "latest": "Neu",
7
- "read_article": "Artikel lesen",
8
- "all_posts": "Alle Beiträge"
9
- }
1
+ {
2
+ "meta_title": "Blog | Daniel Marchi",
3
+ "hero_title": "Blog",
4
+ "hero_description": "Meine Einblicke auf meiner Reise als Entwickler.",
5
+ "latest_article": "Neuester Artikel",
6
+ "latest": "Neu",
7
+ "read_article": "Artikel lesen",
8
+ "all_posts": "Alle Beiträge"
9
+ }