@adaptive-sm/astro-ui 0.2.1 → 0.3.0

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 (92) hide show
  1. package/README.md +7 -7
  2. package/lib/badge/Badge.astro +27 -0
  3. package/lib/badge/classesBadge.ts +67 -0
  4. package/lib/button/Button.astro +17 -27
  5. package/lib/button/buttonCva.ts +2 -2
  6. package/lib/button/buttonIconCva.ts +2 -2
  7. package/lib/button/classesButtonClickAnimation.ts +1 -1
  8. package/lib/button/classesButtonClickAnimationPush.ts +1 -1
  9. package/lib/button/classesButtonClickAnimationSquish.ts +1 -1
  10. package/lib/card/CardWrapper.astro +6 -5
  11. package/lib/card/classesBorderWrapper.ts +1 -0
  12. package/lib/card/classesCardWrapper.ts +21 -9
  13. package/lib/card/classesShadow.ts +5 -0
  14. package/lib/details/Details.astro +13 -8
  15. package/lib/dev/TailwindIndicator.astro +2 -2
  16. package/lib/form/Fieldset.astro +8 -5
  17. package/lib/generate_demo_list/DemoList.astro +3 -3
  18. package/lib/generate_demo_list/generateDemoList.ts +2 -2
  19. package/lib/generate_image_list/generateImageList.ts +3 -3
  20. package/lib/grid/FeatureGridSection.astro +28 -35
  21. package/lib/grid/classesGridCols.ts +27 -2
  22. package/lib/header/PageHeader.astro +25 -0
  23. package/lib/header/SectionHeader.astro +37 -0
  24. package/lib/icon/Icon.astro +26 -0
  25. package/lib/icons/iconGithub.ts +4 -0
  26. package/lib/icons/iconGoogle.ts +4 -0
  27. package/lib/icons/iconLinkedin.ts +2 -0
  28. package/lib/icons/iconNpm.ts +2 -0
  29. package/lib/icons/iconTelegram.ts +2 -0
  30. package/lib/icons/iconTrello.ts +2 -0
  31. package/lib/icons/iconXcom.ts +1 -0
  32. package/lib/img/Img.astro +8 -5
  33. package/lib/img/TypedImg.astro +9 -10
  34. package/lib/img/TypedImgB2.astro +23 -0
  35. package/lib/layouts/MarkdownPageWrapper.astro +19 -0
  36. package/lib/layouts/MinimalLayout.astro +23 -16
  37. package/lib/layouts/parts/ThemeToggle.astro +9 -9
  38. package/lib/link/LinkButton.astro +18 -19
  39. package/lib/link/LinkText.astro +10 -7
  40. package/lib/link/classesTextLink.ts +1 -1
  41. package/lib/list/BlackBulletPoint.astro +1 -1
  42. package/lib/list/BlackBulletPoints.astro +7 -7
  43. package/lib/list/CheckPoint.astro +3 -3
  44. package/lib/list/CheckPoints.astro +3 -3
  45. package/lib/list/NumberedList.astro +1 -1
  46. package/lib/list/Ps.astro +8 -5
  47. package/lib/list/TextOrLink.astro +3 -9
  48. package/lib/md/MarkdownDiv.astro +18 -0
  49. package/lib/modal/Modal.astro +4 -4
  50. package/lib/modal/ModalButton.astro +24 -11
  51. package/lib/page/PageCentered.astro +5 -3
  52. package/lib/page/PageCenteredCard.astro +7 -5
  53. package/lib/page/classesPageCentered.ts +1 -1
  54. package/lib/popover/Popover1.astro +4 -4
  55. package/lib/select/Select.astro +14 -10
  56. package/lib/table/Table.astro +2 -2
  57. package/lib/table/TableD.astro +2 -2
  58. package/lib/table/TableM.astro +2 -2
  59. package/lib/table/TableMEntry.astro +2 -2
  60. package/lib/table/tableVisibilityClasses.ts +3 -3
  61. package/lib/utils/HasId.ts +3 -0
  62. package/lib/utils/HasSubtitle.ts +4 -0
  63. package/lib/utils/HasTitle.ts +4 -0
  64. package/lib/utils/MayHaveButtonVariant.ts +5 -0
  65. package/lib/utils/MayHaveClass.ts +3 -0
  66. package/lib/utils/MayHaveIcon.ts +4 -0
  67. package/lib/utils/MayHaveId.ts +3 -0
  68. package/lib/utils/MayHaveInnerClass.ts +3 -0
  69. package/lib/utils/MayHaveSubtitle.ts +4 -0
  70. package/lib/utils/MayHaveTitle.ts +4 -0
  71. package/package.json +13 -12
  72. package/lib/icon/Icon1.astro +0 -21
  73. package/lib/layouts/MarkdownWrapper.astro +0 -17
  74. package/lib/utils/bun/BunCmd.ts +0 -7
  75. package/lib/utils/bun/cryAndTryAgainLater.ts +0 -6
  76. package/lib/utils/bun/logBunCmd.ts +0 -1
  77. package/lib/utils/bun/runCmdAsync.ts +0 -44
  78. package/lib/utils/bun/runCmdLocally.ts +0 -13
  79. package/lib/utils/obj/objectKeys.ts +0 -21
  80. package/lib/utils/ran/generateId12.ts +0 -7
  81. package/lib/utils/ran/generateId3.ts +0 -7
  82. package/lib/utils/ran/generateId4.ts +0 -7
  83. package/lib/utils/ran/generateId5.ts +0 -7
  84. package/lib/utils/ran/generateId6.ts +0 -7
  85. package/lib/utils/ran/generateId7.ts +0 -7
  86. package/lib/utils/ran/generateReadableId.ts +0 -35
  87. package/lib/utils/ran/urlAlphabet32.ts +0 -8
  88. /package/lib/{layouts/parts → md}/markdown.css +0 -0
  89. /package/lib/utils/{ui/classArr.ts → classArr.ts} +0 -0
  90. /package/lib/utils/{ui/classMerge.ts → classMerge.ts} +0 -0
  91. /package/lib/utils/{ui/isDevEnv.ts → isDevEnv.ts} +0 -0
  92. /package/lib/utils/{ui/tailwindBreakpoint.ts → tailwindBreakpoint.ts} +0 -0
package/README.md CHANGED
@@ -18,7 +18,7 @@ bun add @adaptive-sm/astro-ui
18
18
 
19
19
  ## Tailwind CSS Configuration
20
20
 
21
- To ensure Tailwind scans the library's source files for classes (since components are published as source without a build step), add the following `@source` directive to your project's `src/layouts/global.css` (or equivalent global stylesheet):
21
+ To ensure Tailwind scans the library's source files for classes (since components are published as source without a build step), add the following `@source` directive to your project's `src/layouts/tailwind.css` (or equivalent global stylesheet):
22
22
 
23
23
  ```css
24
24
  @source '/node_modules/@adaptive-sm/astro-ui/lib/**/*.{astro,html,md,mdx,ts,tsx}';
@@ -38,7 +38,7 @@ In your `tsconfig.json`, set up the `~` alias to point to the library:
38
38
  "baseUrl": ".",
39
39
  "paths": {
40
40
  "@/*": ["src/*"],
41
- "~/*": ["node_modules/@adaptive-sm/astro-ui/lib/*"]
41
+ "~ui/*": ["node_modules/@adaptive-sm/astro-ui/lib/*"]
42
42
  }
43
43
  }
44
44
  }
@@ -81,7 +81,7 @@ Import and use components directly in your Astro files. For example:
81
81
 
82
82
  ```astro
83
83
  ---
84
- import { Button } from "~/button/Button.astro"
84
+ import { Button } from "~ui/button/Button.astro"
85
85
  ---
86
86
 
87
87
  <Button variant="primary">Click me</Button>
@@ -91,7 +91,7 @@ import { Button } from "~/button/Button.astro"
91
91
 
92
92
  ```astro
93
93
  ---
94
- import { CardWrapper } from "~/card/CardWrapper.astro"
94
+ import { CardWrapper } from "~ui/card/CardWrapper.astro"
95
95
  ---
96
96
 
97
97
  <CardWrapper> Card content here. </CardWrapper>
@@ -101,7 +101,7 @@ import { CardWrapper } from "~/card/CardWrapper.astro"
101
101
 
102
102
  ```astro
103
103
  ---
104
- import { Img } from "~/img/Img.astro"
104
+ import { Img } from "~ui/img/Img.astro"
105
105
  ---
106
106
 
107
107
  <Img src="/path/to/image.jpg" alt="Description" />
@@ -130,7 +130,7 @@ Refer to individual component documentation in the source code for props and var
130
130
 
131
131
  ### Icons
132
132
 
133
- - [Icon1.astro](lib/icon/Icon1.astro) (replaces SVG icons)
133
+ - [Icon.astro](lib/icon/Icon.astro) (replaces SVG icons)
134
134
 
135
135
  usage:
136
136
 
@@ -153,7 +153,7 @@ usage:
153
153
  ### Layouts
154
154
 
155
155
  - [MinimalLayout.astro](lib/layouts/MinimalLayout.astro)
156
- - [MarkdownWrapper.astro](lib/layouts/MarkdownWrapper.astro)
156
+ - [MarkdownPageWrapper.astro](lib/layouts/MarkdownPageWrapper.astro)
157
157
 
158
158
  ### Navigation bar
159
159
 
@@ -0,0 +1,27 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types"
3
+ import { type BadgeVariant, badgeCva, badgeIconCva, badgeVariant } from "~ui/badge/classesBadge"
4
+ import Icon from "~ui/icon/Icon.astro"
5
+ import type { MayHaveClass } from "~ui/utils/MayHaveClass"
6
+
7
+ interface Props extends MayHaveClass {
8
+ id?: string
9
+ variant?: BadgeVariant
10
+
11
+ icon?: string
12
+ iconRight?: string
13
+ iconClass?: string
14
+
15
+ class?: string
16
+ restProps?: HTMLAttributes<"svg">
17
+ }
18
+
19
+ const p = Astro.props
20
+ const v = p.variant ?? badgeVariant.outline
21
+ ---
22
+
23
+ <span id={p.id} class={badgeCva(v, p.class)} {...p.restProps}>
24
+ {p.icon && <Icon path={p.icon} class={badgeIconCva(v, p.iconClass)} />}
25
+ <slot />
26
+ {p.iconRight && <Icon path={p.iconRight} class={badgeIconCva(v, p.iconClass)} />}
27
+ </span>
@@ -0,0 +1,67 @@
1
+ import { classArr } from "~ui/utils/classArr"
2
+ import { classMerge } from "~ui/utils/classMerge"
3
+
4
+ export type BadgeVariant = keyof typeof badgeVariant
5
+
6
+ export const badgeVariant = {
7
+ base: "base",
8
+ contrast: "contrast",
9
+ outline: "outline",
10
+ } as const
11
+
12
+ export const classesBadgeBase = classArr(
13
+ "inline-flex items-center", // layout
14
+ "text-sm", // text
15
+ "px-2.5 py-0.5 rounded-full", // padding
16
+ "flex gap-2", // layout children
17
+ )
18
+
19
+ export const classesBadgeContrast = classArr(
20
+ classesBadgeBase,
21
+ "text-white dark:text-slate-900 dark:hover:text-slate-900", // text
22
+ "bg-slate-900 dark:bg-slate-50", // bg
23
+ )
24
+
25
+ export const classesBadgeOutline = classArr(
26
+ classesBadgeBase,
27
+ "border border-gray-400 dark:border-gray-500", // border
28
+ )
29
+
30
+ export const classesBadgeVariant = {
31
+ base: classesBadgeBase,
32
+ contrast: classesBadgeContrast,
33
+ outline: classesBadgeOutline,
34
+ } as const satisfies Record<BadgeVariant, string>
35
+
36
+ export function badgeCva(v: BadgeVariant, classes?: string): string {
37
+ return classMerge(classesBadgeVariant[v ?? badgeVariant.base], classes)
38
+ }
39
+
40
+ const classesTextFillBlack = "text-black fill-black"
41
+ const classesTextFillWhite = "text-white fill-white"
42
+
43
+ const classesBlackWhite = classArr(
44
+ classesTextFillBlack,
45
+ "dark:text-white", // dark text
46
+ "dark:fill-white", // dark fill
47
+ )
48
+
49
+ const classesWhiteWhite = classArr(
50
+ classesTextFillWhite,
51
+ "dark:text-white", // dark text
52
+ "dark:fill-white", // dark fill
53
+ )
54
+
55
+ const baseClasses = classArr(
56
+ "size-6", // size
57
+ )
58
+
59
+ const badgeIconClasses = {
60
+ base: classesBlackWhite,
61
+ outline: classesBlackWhite,
62
+ contrast: classArr(classesTextFillWhite, "dark:text-black dark:fill-black"),
63
+ } as const satisfies Record<BadgeVariant, string>
64
+
65
+ export function badgeIconCva(variant: BadgeVariant, classes?: string) {
66
+ return classMerge(baseClasses, badgeIconClasses[variant], classes)
67
+ }
@@ -1,9 +1,10 @@
1
1
  ---
2
- import { buttonCva2, type ButtonSize, type ButtonVariant } from "~/button/buttonCva"
3
- import { buttonIconCva } from "~/button/buttonIconCva"
4
- import { classesButtonClickAnimation } from "~/button/classesButtonClickAnimation"
5
- import Icon1 from "~/icon/Icon1.astro"
6
- import { classMerge } from "~/utils/ui/classMerge"
2
+ import type { HTMLAttributes } from "astro/types"
3
+ import { buttonCva2, type ButtonSize, type ButtonVariant } from "~ui/button/buttonCva"
4
+ import { buttonIconCva } from "~ui/button/buttonIconCva"
5
+ import { classesButtonClickAnimation } from "~ui/button/classesButtonClickAnimation"
6
+ import Icon from "~ui/icon/Icon.astro"
7
+ import { classMerge } from "~ui/utils/classMerge"
7
8
 
8
9
  interface Props {
9
10
  id?: string
@@ -18,41 +19,30 @@ interface Props {
18
19
  iconRight?: string
19
20
  iconClass?: string
20
21
  class?: string
21
- onclick?: any
22
+ restProps?: HTMLAttributes<"button">
22
23
  }
23
- const props = Astro.props
24
+ const p = Astro.props
24
25
 
25
- const classes = buttonCva2(props.variant, props.size, classesButtonClickAnimation, props.class)
26
+ const classes = buttonCva2(p.variant, p.size, classesButtonClickAnimation, p.class)
26
27
  const hasChildren = Astro.slots.has("default")
27
- const hasContent = hasChildren || props.text
28
+ const hasContent = hasChildren || p.text
28
29
  ---
29
30
 
30
- <button id={props.id} title={props.title} class={classes} onclick={props.onclick}>
31
+ <button id={p.id} title={p.title} class={classes} {...p.restProps}>
32
+ {p.icon && <Icon path={p.icon} class={classMerge(hasContent && "mr-2", buttonIconCva(p.variant, p.iconClass))} />}
31
33
  {
32
- props.icon && (
33
- <Icon1
34
- path={props.icon}
35
- class={classMerge(hasContent && "mr-2", buttonIconCva(props.variant, props.iconClass))}
36
- />
37
- )
38
- }
39
- {
40
- props.text && (
41
- <span id={props.contentId} class={props.contentClass}>
42
- {props.text}
34
+ p.text && (
35
+ <span id={p.contentId} class={p.contentClass}>
36
+ {p.text}
43
37
  </span>
44
38
  )
45
39
  }
46
40
  {
47
41
  hasContent && (
48
- <span id={props.contentId} class={props.contentClass}>
42
+ <span id={p.contentId} class={p.contentClass}>
49
43
  <slot />
50
44
  </span>
51
45
  )
52
46
  }
53
- {
54
- props.iconRight && (
55
- <Icon1 path={props.iconRight} class={buttonIconCva(props.variant, hasContent && "ml-2", props.iconClass)} />
56
- )
57
- }
47
+ {p.iconRight && <Icon path={p.iconRight} class={buttonIconCva(p.variant, hasContent && "ml-2", p.iconClass)} />}
58
48
  </button>
@@ -1,5 +1,5 @@
1
- import { classArr } from "~/utils/ui/classArr"
2
- import { classMerge } from "~/utils/ui/classMerge"
1
+ import { classArr } from "~ui/utils/classArr"
2
+ import { classMerge } from "~ui/utils/classMerge"
3
3
  import { classesButtonClickAnimation } from "./classesButtonClickAnimation"
4
4
  import { classesButtonDisabled } from "./classesButtonDisabled"
5
5
 
@@ -1,5 +1,5 @@
1
- import { classArr } from "~/utils/ui/classArr"
2
- import { classMerge } from "~/utils/ui/classMerge"
1
+ import { classArr } from "~ui/utils/classArr"
2
+ import { classMerge } from "~ui/utils/classMerge"
3
3
  import { type ButtonVariant } from "./buttonCva"
4
4
 
5
5
  const classesTextFillBlack = "text-black fill-black"
@@ -1,4 +1,4 @@
1
- import { classArr } from "~/utils/ui/classArr"
1
+ import { classArr } from "~ui/utils/classArr"
2
2
 
3
3
  export const classesButtonClickAnimation = classArr(
4
4
  // "duration-300",
@@ -1,4 +1,4 @@
1
- import { classArr } from "~/utils/ui/classArr"
1
+ import { classArr } from "~ui/utils/classArr"
2
2
 
3
3
  /**
4
4
  * disable hover because popovers move with it
@@ -1,4 +1,4 @@
1
- import { classArr } from "~/utils/ui/classArr"
1
+ import { classArr } from "~ui/utils/classArr"
2
2
 
3
3
  /**
4
4
  * unused because it's a little to fancy
@@ -1,15 +1,16 @@
1
1
  ---
2
- import { classesCardWrapperP4 } from "~/card/classesCardWrapper"
3
- import { classArr } from "~/utils/ui/classArr"
4
- import { classMerge } from "~/utils/ui/classMerge"
2
+ import type { HTMLAttributes } from "astro/types"
3
+ import { classesCardWrapperP4 } from "~ui/card/classesCardWrapper"
4
+ import { classMerge } from "~ui/utils/classMerge"
5
5
 
6
6
  interface Props {
7
- id?:string
7
+ id?: string
8
8
  class?: string
9
+ restProps?: HTMLAttributes<"div">
9
10
  }
10
11
  const p = Astro.props
11
12
  ---
12
13
 
13
- <div id={p.id} class={classMerge(classesCardWrapperP4, p.class)}>
14
+ <div id={p.id} class={classMerge(classesCardWrapperP4, p.class)} {...p.restProps}>
14
15
  <slot />
15
16
  </div>
@@ -0,0 +1 @@
1
+ export const classesBorderWrapper = "border border-gray-500 rounded-xl"
@@ -1,16 +1,28 @@
1
- import { classArr } from "~/utils/ui/classArr"
1
+ import { classesShadowMdLg } from "~ui/card/classesShadow"
2
+ import { classArr } from "~ui/utils/classArr"
3
+
4
+ export const classesCardWrapperBorderDark = "dark:border dark:border-gray-500"
5
+ export const classesCardWrapperBg = "bg-white dark:bg-zinc-800"
2
6
 
3
7
  export const classesCardWrapper = classArr(
4
- "rounded-lg shadow-lg", // card shadows/padding
5
- "bg-white dark:bg-gray-900", // bg
8
+ "rounded-lg",
9
+ classesShadowMdLg, // card shadows/padding
10
+ classesCardWrapperBg, // bg
11
+ classesCardWrapperBorderDark, // border
6
12
  )
7
13
 
8
- export const classesCardWrapperP4 = classArr(classesCardWrapper, "p-4 lg:p-8")
9
- export const classesCardWrapperP8 = classArr(classesCardWrapper, "p-4 sm:p-8")
14
+ export const classesPadding4Lg8 = "p-4 lg:p-8"
15
+ export const classesPadding4sm8 = "p-4 sm:p-8"
16
+ export const classesPadding4sm8md12 = "p-4 sm:p-8 md:p-12"
17
+
18
+ export const classesCardWrapperP4 = classArr(classesCardWrapper, classesPadding4Lg8)
19
+ export const classesCardWrapperP8 = classArr(classesCardWrapper, classesPadding4sm8)
20
+
21
+ export const classesRoundedShadowXl = "rounded-xl shadow-xl"
10
22
 
11
23
  export const classesCardWrapperPage = classArr(
12
- "rounded-xl shadow-xl", // rounded border + shadow
13
- "p-4 sm:p-8 md:p-12", // padding
14
- // "sm:mx-auto", // center
15
- "bg-white dark:bg-gray-900", // bg
24
+ classesRoundedShadowXl, // rounded border + shadow
25
+ classesPadding4sm8md12, // padding
26
+ classesCardWrapperBg, // bg
27
+ classesCardWrapperBorderDark, // border
16
28
  )
@@ -0,0 +1,5 @@
1
+ export const classesShadowSmMd = "shadow-sm hover:shadow-md transition duration-500"
2
+
3
+ export const classesShadowMdLg = "shadow-md hover:shadow-lg transition duration-500"
4
+
5
+ export const classesShadowLgXl = "shadow-lg hover:shadow-xl transition duration-500"
@@ -1,8 +1,9 @@
1
1
  ---
2
- import Icon1 from "~/icon/Icon1.astro"
3
- import { classArr } from "~/utils/ui/classArr"
4
- import { classMerge } from "~/utils/ui/classMerge"
2
+ import Icon from "~ui/icon/Icon.astro"
3
+ import { classArr } from "~ui/utils/classArr"
4
+ import { classMerge } from "~ui/utils/classMerge"
5
5
  import { mdiChevronDown } from "@mdi/js"
6
+ import type { HTMLAttributes } from "astro/types"
6
7
 
7
8
  interface Props {
8
9
  icon?: string
@@ -10,6 +11,8 @@ interface Props {
10
11
  subtitle?: string
11
12
  class?: string
12
13
  summaryClass?: string
14
+ restProps?: HTMLAttributes<"details">
15
+ summaryProps?: HTMLAttributes<"summary">
13
16
  }
14
17
  const p = Astro.props
15
18
  const icon = p.icon
@@ -27,6 +30,7 @@ const hasSummary = Astro.slots.has("summary")
27
30
  "overflow-hidden",
28
31
  p.class,
29
32
  )}
33
+ {...p.restProps}
30
34
  >
31
35
  <summary
32
36
  class={classArr(
@@ -35,20 +39,21 @@ const hasSummary = Astro.slots.has("summary")
35
39
  "cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors",
36
40
  p.summaryClass,
37
41
  )}
42
+ {...p.summaryProps}
38
43
  >
39
44
  {
40
45
  !hasSummary && title && (
41
46
  <>
42
- {icon && <Icon1 path={icon} class="size-7 mt-1" />}
43
- <div class="flex-1">
47
+ {icon && <Icon path={icon} class="size-7 mt-1" />}
48
+ <span class="flex-1">
44
49
  <h3 class={"text-xl font-semibold"}>{title}</h3>
45
- {subtitle && <p class={"text-muted-foreground mt-1"}>{subtitle}</p>}
46
- </div>
50
+ {subtitle && <span class={"text-muted-foreground mt-1"}>{subtitle}</span>}
51
+ </span>
47
52
  </>
48
53
  )
49
54
  }
50
55
  <slot name="summary" />
51
- <Icon1
56
+ <Icon
52
57
  path={mdiChevronDown}
53
58
  class={classArr("size-7", "text-gray-400 dark:text-gray-600", "transition-transform group-open:rotate-180")}
54
59
  />
@@ -1,6 +1,6 @@
1
1
  ---
2
- import { classMerge } from "~/utils/ui/classMerge"
3
- import { isDevEnv } from "~/utils/ui/isDevEnv"
2
+ import { classMerge } from "~ui/utils/classMerge"
3
+ import { isDevEnv } from "~ui/utils/isDevEnv"
4
4
  ---
5
5
 
6
6
  {
@@ -1,6 +1,7 @@
1
1
  ---
2
+ import type { HTMLAttributes } from "astro/types"
2
3
  import { classesFieldset } from "./classesFieldset"
3
- import { classMerge } from "~/utils/ui/classMerge"
4
+ import { classMerge } from "~ui/utils/classMerge"
4
5
 
5
6
  interface Props {
6
7
  title: string
@@ -8,13 +9,15 @@ interface Props {
8
9
  subtitles?: string | string[]
9
10
  subtitleClass?: string
10
11
  class?: string
12
+ restProps?: HTMLAttributes<"fieldset">
13
+ legendProps?: HTMLAttributes<"legend">
11
14
  }
12
- const props = Astro.props
15
+ const p = Astro.props
13
16
  ---
14
17
 
15
- <fieldset class={classMerge(classesFieldset, props.class)}>
16
- <legend class={classMerge("px-1", props.titleClass)}>
17
- {props.title}
18
+ <fieldset class={classMerge(classesFieldset, p.class)} {...p.restProps}>
19
+ <legend class={classMerge("px-1", p.titleClass)} {...p.legendProps}>
20
+ {p.title}
18
21
  <!--{props.subtitles && <PopoverI title={props.title} points={props.subtitles} />}-->
19
22
  </legend>
20
23
  <slot />
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { classesTextLink } from "~/link/classesTextLink"
3
- import { classArr } from "~/utils/ui/classArr"
2
+ import { classesTextLink } from "~ui/link/classesTextLink"
3
+ import { classArr } from "~ui/utils/classArr"
4
4
  import { type DemoListType } from "./DemoListType"
5
- import { classMerge } from "~/utils/ui/classMerge"
5
+ import { classMerge } from "~ui/utils/classMerge"
6
6
 
7
7
  interface Props {
8
8
  demos: DemoListType
@@ -1,6 +1,6 @@
1
1
  import { readdir, writeFile } from "node:fs/promises"
2
2
  import { join } from "node:path"
3
- import { runCmdAsync } from "~/utils/bun/runCmdAsync"
3
+ import { runCmdAsync } from "~utils/bun/runCmdAsync"
4
4
  import type { DemoListType } from "./DemoListType"
5
5
 
6
6
  export async function generateDemoList(demosPath: string, outputPath: string) {
@@ -20,7 +20,7 @@ export async function generateDemoList(demosPath: string, outputPath: string) {
20
20
  }
21
21
  sortDemoPageList(demoPageList)
22
22
 
23
- const outputContent = `import type { DemoListType } from "~/generate_demo_list/DemoListType"
23
+ const outputContent = `import type { DemoListType } from "~ui/generate_demo_list/DemoListType"
24
24
 
25
25
  export const demoList = ${JSON.stringify(demoPageList, null, 2)} satisfies DemoListType;
26
26
  `
@@ -1,8 +1,8 @@
1
1
  import imageSize from "image-size"
2
2
  import { promises as fs } from "node:fs"
3
3
  import path from "node:path"
4
- import type { ImageType } from "~/img/ImageType"
5
- import { runCmdAsync } from "~/utils/bun/runCmdAsync"
4
+ import type { ImageType } from "~ui/img/ImageType"
5
+ import { runCmdAsync } from "~utils/bun/runCmdAsync"
6
6
 
7
7
  const IMAGE_EXTENSIONS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif", ".tiff", ".svg"])
8
8
 
@@ -42,7 +42,7 @@ export async function generateImageList(
42
42
 
43
43
  async function writeGeneratedImagesFile(imageMap: Record<string, ImageType>, outputPath: string): Promise<void> {
44
44
  const outputContent = `
45
- import type { ImageType } from "~/img/ImageType"
45
+ import type { ImageType } from "~ui/img/ImageType"
46
46
  // Auto-generated, manual changes will be lost
47
47
  export const imageList = ${JSON.stringify(imageMap, null, 2)} as const satisfies Record<string, ImageType>;
48
48
  `
@@ -1,39 +1,52 @@
1
1
  ---
2
- import Icon1 from "~/icon/Icon1.astro"
3
- import { classArr } from "~/utils/ui/classArr"
2
+ import Icon from "~ui/icon/Icon.astro"
3
+ import { classArr } from "~ui/utils/classArr"
4
4
  import { mdiLink } from "@mdi/js"
5
5
  import type { GridFeatureType } from "./GridFeatureType"
6
- import { classMerge } from "~/utils/ui/classMerge"
6
+ import { classMerge } from "~ui/utils/classMerge"
7
+ import type { HTMLAttributes } from "astro/types"
8
+ import type { CssProperties } from "node_modules/astro/dist/assets/fonts/definitions"
9
+ import { classesShadowMdLg } from "~ui/card/classesShadow"
7
10
 
8
11
  interface Props {
9
12
  id: string
10
13
  title: string
14
+ titleClass?: string
11
15
  subtitle1: string
16
+ subtitle1Class?: string
12
17
  subtitle2?: string
18
+ subtitle2Class?: string
13
19
  features: GridFeatureType[]
14
20
  class?: string
21
+ headerClass?: string
15
22
  gridClass?: string
16
23
  cardClass?: string
24
+ restProps?: HTMLAttributes<"section">
25
+ style?: CssProperties
17
26
  }
18
27
  const p = Astro.props
19
28
  ---
20
29
 
21
- <section id={p.id} class={classMerge("w-full", "py-25 px-4", p.class)}>
22
- <div class={classArr("max-w-7xl mx-auto", "text-center mb-8")}>
30
+ <section id={p.id} class={classMerge("w-full", "py-25 px-4", p.class)} style={p.style} {...p.restProps}>
31
+ <div class={classArr("max-w-7xl mx-auto", "text-center mb-8", p.headerClass)}>
23
32
  <h2 class={classArr("text-3xl sm:text-4xl font-bold", "mb-4", "group flex items-center justify-center")}>
24
33
  <span>{p.title}</span>
25
- <button
26
- onclick={`copyLink('${p.id}')`}
27
- class="opacity-0 group-hover:opacity-100 ml-2 p-1 rounded hidden sm:flex"
28
- title="Copy link to this section"
34
+ <a
35
+ href={"#" + p.id}
36
+ class="opacity-0 group-hover:opacity-100 p-1 rounded hidden sm:flex"
37
+ title="Link to this section"
29
38
  >
30
- <Icon1 path={mdiLink} class="size-4" />
31
- </button>
39
+ <Icon path={mdiLink} class="size-6 fill-gray-500 dark:fill-gray-500" />
40
+ </a>
32
41
  </h2>
33
- <p class="text-xl text-muted-foreground mx-auto">
42
+ <p class={classMerge("text-xl text-muted-foreground mx-auto", p.subtitle1Class)}>
34
43
  {p.subtitle1}
35
44
  </p>
36
- {p.subtitle2 && <p class="text-xl text-muted-foreground mx-auto mt-1">{p.subtitle2}</p>}
45
+ {
46
+ p.subtitle2 && (
47
+ <p class={classMerge("text-xl text-muted-foreground mx-auto mt-1", p.subtitle2Class)}>{p.subtitle2}</p>
48
+ )
49
+ }
37
50
  </div>
38
51
 
39
52
  <div class={classMerge("grid md:grid-cols-2 lg:grid-cols-3 gap-8", "max-w-7xl mx-auto", p.gridClass)}>
@@ -44,12 +57,12 @@ const p = Astro.props
44
57
  "bg-white dark:bg-gray-800", // bg
45
58
  "rounded-lg p-6", // padding
46
59
  // "border", // border
47
- "shadow-sm hover:shadow-lg transition-shadow", // shadow
60
+ classesShadowMdLg,
48
61
  p.cardClass,
49
62
  )}
50
63
  >
51
64
  <div class="flex items-center gap-3 mb-4">
52
- <Icon1 path={feature.icon} class="size-8" />
65
+ <Icon path={feature.icon} class="size-8" />
53
66
  <h3 class="text-xl font-semibold">{feature.title}</h3>
54
67
  </div>
55
68
  <p class="text-muted-foreground">{feature.description}</p>
@@ -58,23 +71,3 @@ const p = Astro.props
58
71
  }
59
72
  </div>
60
73
  </section>
61
-
62
- <script>
63
- declare global {
64
- interface Window {
65
- copyLink?: (id: string) => void
66
- }
67
- }
68
- window.copyLink = function (id: string) {
69
- const url = window.location.href.split("#")[0] + "#" + id
70
- navigator.clipboard
71
- .writeText(url)
72
- .then(() => {
73
- // Optional: show a toast or feedback
74
- console.log("Link copied:", url)
75
- })
76
- .catch((err) => {
77
- console.error("Failed to copy link:", err)
78
- })
79
- }
80
- </script>
@@ -1,14 +1,33 @@
1
- import { classArr } from "~/utils/ui/classArr"
1
+ import { classArr } from "~ui/utils/classArr"
2
2
 
3
3
  export const classesGridCols2ContentFr = "grid grid-cols-[max-content_1fr]"
4
4
  export const classesGridCols3MaxMinFr = "grid grid-cols-[max-content_min-content_1fr]"
5
5
 
6
+ export const classesGridCols2sm = classArr("grid grid-cols-1", "sm:grid-cols-2")
7
+
8
+ export const classesGridCols2md = classArr("grid grid-cols-1", "md:grid-cols-2")
9
+
10
+ /**
11
+ * 1280/2 = 640 px = 35.5 rem / col
12
+ */
13
+ export const classesGridCols2lg = classArr("grid grid-cols-1", "lg:grid-cols-2")
14
+
6
15
  /**
7
16
  * ~600 px (37rem, ~60ch) / col
8
17
  */
9
18
  export const classesGridCols2xl = classArr("grid grid-cols-1", "xl:grid-cols-2")
10
19
  export const classesGridCols2xl3 = classArr(classesGridCols2xl, "2xl:grid-cols-4", "3xl:grid-cols-6")
11
20
 
21
+ /**
22
+ * 1024/3 = 341 px = 19rem / col
23
+ * xl: 1280/340 = 3.75
24
+ * lg: 1024/340 = 3
25
+ * md: 768/340 = 2.26
26
+ * sm: 640/340 = 1.8
27
+ * xs: 400/340 = 1.17
28
+ */
29
+ export const classesGridCols3lg = classArr("grid grid-cols-1", "md:grid-cols-2", "lg:grid-cols-3")
30
+
12
31
  /**
13
32
  * ~400 px (25rem, ~40ch) / col
14
33
  */
@@ -36,7 +55,13 @@ export const classesGridCols4xl3 = classArr(classesGridCols4xl, "2xl:grid-cols-8
36
55
  * sm: 640/256 = 2.5
37
56
  * xs: 400/256 = 1.56
38
57
  */
39
- export const classesGridCols5xl = classArr("grid grid-cols-1", "sm:grid-cols-2", "md:grid-cols-3", "lg:grid-cols-4", "xl:grid-cols-5")
58
+ export const classesGridCols5xl = classArr(
59
+ "grid grid-cols-1",
60
+ "sm:grid-cols-2",
61
+ "md:grid-cols-3",
62
+ "lg:grid-cols-4",
63
+ "xl:grid-cols-5",
64
+ )
40
65
  export const classesGridCols5xl3 = classArr(classesGridCols5xl, "2xl:grid-cols-10", "3xl:grid-cols-15")
41
66
 
42
67
  /**