@draftlab/auth 0.0.4 → 0.1.1

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.
@@ -1,233 +1,34 @@
1
+ import { Theme } from "../themes/theme.js";
2
+
1
3
  //#region src/ui/select.d.ts
4
+
2
5
  /**
3
- * Pre-built UI component for OAuth provider selection.
4
- * Displays a clean selection interface where users can choose their preferred authentication method.
5
- *
6
- * ## Features
7
- *
8
- * - **Multiple providers**: Support for popular OAuth providers with built-in icons
9
- * - **Customizable displays**: Override provider names and copy text
10
- * - **Conditional visibility**: Hide specific providers using configuration
11
- * - **Type safety**: Full TypeScript support with autocomplete for known providers
12
- * - **Accessibility**: Proper ARIA labels and semantic markup
13
- *
14
- * ## Usage
15
- *
16
- * ```ts
17
- * import { Select } from "@draftauth/core/ui/select"
18
- *
19
- * export default issuer({
20
- * select: Select({
21
- * copy: {
22
- * button_provider: "Continuar com"
23
- * },
24
- * displays: {
25
- * code: "Código PIN",
26
- * github: "Conta GitHub"
27
- * },
28
- * providers: {
29
- * github: {
30
- * hide: true
31
- * },
32
- * google: {
33
- * display: "Google"
34
- * }
35
- * }
36
- * })
37
- * // ...
38
- * })
39
- * ```
40
- *
41
- * ## Provider Configuration
42
- *
43
- * Each provider can be individually configured:
44
- *
45
- * - **hide**: Boolean to control visibility
46
- * - **display**: Custom display name override
47
- *
48
- * Global display overrides are applied through the `displays` property, which affects
49
- * all providers of that type unless specifically overridden at the provider level.
50
- *
51
- * @packageDocumentation
52
- */
53
- /**
54
- * Default copy text used throughout the select UI.
55
- * These values are used when no custom copy is provided.
56
- */
57
- declare const DEFAULT_COPY: {
58
- /**
59
- * Copy for the provider button prefix text.
60
- * @example "Continue with GitHub" where "Continue with" is this value
61
- */
62
- button_provider: string;
63
- };
64
- /**
65
- * Default display names for all known provider types.
66
- * These provide consistent naming across the application and serve as fallbacks
67
- * when no custom display name is configured.
68
- */
69
- declare const DEFAULT_DISPLAY: {
70
- steam: string;
71
- twitch: string;
72
- google: string;
73
- github: string;
74
- apple: string;
75
- code: string;
76
- x: string;
77
- facebook: string;
78
- microsoft: string;
79
- slack: string;
80
- password: string;
81
- };
82
- /**
83
- * Customizable copy text for the Select UI component.
84
- * Allows overriding default text to support internationalization or custom branding.
85
- */
86
- type SelectCopy = typeof DEFAULT_COPY;
87
- /**
88
- * Union type of all known provider types that have default display names and icons.
89
- * These types get autocompletion support in the displays configuration.
90
- */
91
- type KnownProviderType = keyof typeof DEFAULT_DISPLAY;
92
- /**
93
- * Configuration options for individual providers in the select UI.
94
- * Each provider can be individually controlled and customized.
6
+ * Provider configuration for the select UI
95
7
  */
96
8
  interface ProviderConfig {
97
- /**
98
- * Whether to hide the provider from the select UI.
99
- * When true, the provider will not appear in the selection interface.
100
- *
101
- * @default false
102
- */
9
+ /** Whether to hide the provider from the select UI */
103
10
  hide?: boolean;
104
- /**
105
- * Custom display name for this specific provider instance.
106
- * Overrides both the default display name and any global display override.
107
- *
108
- * @example "My Custom Google Login"
109
- */
11
+ /** Custom display name for this provider */
110
12
  display?: string;
111
13
  }
112
14
  /**
113
- * Configuration properties for the Select UI component.
114
- * Provides comprehensive customization of appearance and behavior.
15
+ * Props for the Select component
115
16
  */
116
17
  interface SelectProps {
117
- /**
118
- * Provider-specific configurations mapped by provider key.
119
- * Each key corresponds to a provider instance, allowing individual customization.
120
- *
121
- * @example
122
- * ```ts
123
- * {
124
- * github: {
125
- * hide: true
126
- * },
127
- * google: {
128
- * display: "Google Account"
129
- * },
130
- * "custom-provider": {
131
- * display: "Custom OAuth"
132
- * }
133
- * }
134
- * ```
135
- */
18
+ /** Provider-specific configurations */
136
19
  providers?: Record<string, ProviderConfig>;
137
- /**
138
- * Custom copy text overrides for UI elements.
139
- * Useful for internationalization and custom branding.
140
- *
141
- * @example
142
- * ```ts
143
- * {
144
- * button_provider: "Sign in with"
145
- * }
146
- * ```
147
- */
148
- copy?: Partial<SelectCopy>;
149
- /**
150
- * Global display name overrides for provider types.
151
- *
152
- * This allows you to override the default display names globally for all providers
153
- * of a specific type. For known provider types, you get full autocompletion support.
154
- * Custom provider types are also supported.
155
- *
156
- * The priority order for display names is:
157
- * 1. Provider-specific `display` in `providers` config
158
- * 2. Type-specific `displays` override (this property)
159
- * 3. Default display name from `DEFAULT_DISPLAY`
160
- * 4. Provider type string as fallback
161
- *
162
- * @example
163
- * ```ts
164
- * {
165
- * displays: {
166
- * code: "PIN Code", // ✅ Autocomplete available
167
- * github: "GitHub Account", // ✅ Autocomplete available
168
- * customType: "Custom" // ✅ Also works for unknown types
169
- * }
170
- * }
171
- * ```
172
- */
173
- displays?: Partial<Record<KnownProviderType, string>> & Record<string, string>;
20
+ /** Custom copy text overrides */
21
+ copy?: {
22
+ button_provider?: string;
23
+ };
24
+ /** Global display name overrides for provider types */
25
+ displays?: Record<string, string>;
26
+ /** Theme configuration */
27
+ theme?: Theme;
174
28
  }
175
29
  /**
176
- * Creates a provider selection UI component for OAuth authentication.
177
- *
178
- * This component generates a complete authentication provider selection interface that:
179
- * - Displays available OAuth providers as clickable buttons
180
- * - Includes appropriate icons for recognized providers
181
- * - Supports custom theming and internationalization
182
- * - Provides accessible markup with proper ARIA attributes
183
- * - Handles provider visibility and custom display names
184
- *
185
- * @param props - Configuration options for customizing the select UI
186
- * @returns An async function that generates the HTML response for the selection interface
187
- *
188
- * @example
189
- * ```ts
190
- * // Basic usage with defaults
191
- * const selectUI = Select()
192
- *
193
- * // Customized with provider configuration
194
- * const selectUI = Select({
195
- * copy: {
196
- * button_provider: "Sign in with"
197
- * },
198
- * displays: {
199
- * github: "GitHub Account",
200
- * code: "Email Code"
201
- * },
202
- * providers: {
203
- * facebook: { hide: true },
204
- * google: { display: "Google Workspace" }
205
- * }
206
- * })
207
- *
208
- * // Use in issuer configuration
209
- * export default issuer({
210
- * select: selectUI,
211
- * // ... other configuration
212
- * })
213
- * ```
214
- *
215
- * ## Provider Resolution Logic
216
- *
217
- * Display names are resolved in the following priority order:
218
- * 1. Provider-specific `display` property
219
- * 2. Global `displays` type override
220
- * 3. Default display name from `DEFAULT_DISPLAY`
221
- * 4. Provider type string as final fallback
222
- *
223
- * ## Accessibility Features
224
- *
225
- * The generated UI includes:
226
- * - Semantic HTML structure
227
- * - Proper button roles and keyboard navigation
228
- * - Icon accessibility with `aria-hidden="true"`
229
- * - Screen reader friendly provider names
30
+ * Creates a provider selection UI
230
31
  */
231
- declare const Select: (props?: SelectProps) => (providers: Record<string, string>, _req: Request) => Promise<Response>;
32
+ declare const Select: (props?: SelectProps) => (providers: Record<string, string>) => Promise<Response>;
232
33
  //#endregion
233
- export { KnownProviderType, ProviderConfig, Select, SelectCopy, SelectProps };
34
+ export { ProviderConfig, Select, SelectProps };
package/dist/ui/select.js CHANGED
@@ -1,277 +1,90 @@
1
- import { Layout } from "./base.js";
1
+ import { Layout, renderToHTML } from "./base.js";
2
2
  import { ICON_GITHUB, ICON_GOOGLE } from "./icon.js";
3
+ import { jsx, jsxs } from "preact/jsx-runtime";
3
4
 
4
- //#region src/ui/select.ts
5
+ //#region src/ui/select.tsx
5
6
  /**
6
- * Default copy text used throughout the select UI.
7
- * These values are used when no custom copy is provided.
7
+ * Icon components for providers
8
8
  */
9
- const DEFAULT_COPY = { button_provider: "Continue with" };
9
+ const PROVIDER_ICONS = {
10
+ github: ICON_GITHUB,
11
+ google: ICON_GOOGLE,
12
+ code: () => /* @__PURE__ */ jsx("svg", {
13
+ width: "20",
14
+ height: "20",
15
+ viewBox: "0 0 52 52",
16
+ fill: "currentColor",
17
+ "aria-label": "Code authentication",
18
+ role: "img",
19
+ children: /* @__PURE__ */ jsx("path", { d: "M8.55,36.91A6.55,6.55,0,1,1,2,43.45,6.54,6.54,0,0,1,8.55,36.91Zm17.45,0a6.55,6.55,0,1,1-6.55,6.54A6.55,6.55,0,0,1,26,36.91Zm17.45,0a6.55,6.55,0,1,1-6.54,6.54A6.54,6.54,0,0,1,43.45,36.91ZM8.55,19.45A6.55,6.55,0,1,1,2,26,6.55,6.55,0,0,1,8.55,19.45Zm17.45,0A6.55,6.55,0,1,1,19.45,26,6.56,6.56,0,0,1,26,19.45Zm17.45,0A6.55,6.55,0,1,1,36.91,26,6.55,6.55,0,0,1,43.45,19.45ZM8.55,2A6.55,6.55,0,1,1,2,8.55,6.54,6.54,0,0,1,8.55,2ZM26,2a6.55,6.55,0,1,1-6.55,6.55A6.55,6.55,0,0,1,26,2ZM43.45,2a6.55,6.55,0,1,1-6.54,6.55A6.55,6.55,0,0,1,43.45,2Z" })
20
+ })
21
+ };
10
22
  /**
11
- * Default display names for all known provider types.
12
- * These provide consistent naming across the application and serve as fallbacks
13
- * when no custom display name is configured.
23
+ * Default display names for provider types
14
24
  */
15
- const DEFAULT_DISPLAY = {
16
- steam: "Steam",
17
- twitch: "Twitch",
18
- google: "Google",
25
+ const DEFAULT_DISPLAYS = {
19
26
  github: "GitHub",
20
- apple: "Apple",
27
+ google: "Google",
21
28
  code: "Code",
22
- x: "X",
23
- facebook: "Facebook",
24
- microsoft: "Microsoft",
25
- slack: "Slack",
26
29
  password: "Password"
27
30
  };
28
31
  /**
29
- * Comprehensive icon mapping for all supported authentication providers.
30
- * Each icon is an optimized SVG component with proper accessibility attributes.
31
- *
32
- * Icons are designed to:
33
- * - Scale properly at different sizes
34
- * - Inherit text color for theming
35
- * - Include proper ARIA attributes
36
- * - Work with screen readers
32
+ * Individual provider button component
37
33
  */
38
- const ICON = {
39
- steam: `
40
- <svg
41
- aria-hidden="true"
42
- class="bi bi-steam"
43
- fill="currentColor"
44
- height="16"
45
- viewBox="0 0 16 16"
46
- width="16"
47
- xmlns="http://www.w3.org/2000/svg"
48
- >
49
- <path d="M.329 10.333A8.01 8.01 0 0 0 7.99 16C12.414 16 16 12.418 16 8s-3.586-8-8.009-8A8.006 8.006 0 0 0 0 7.468l.003.006 4.304 1.769A2.2 2.2 0 0 1 5.62 8.88l1.96-2.844-.001-.04a3.046 3.046 0 0 1 3.042-3.043 3.046 3.046 0 0 1 3.042 3.043 3.047 3.047 0 0 1-3.111 3.044l-2.804 2a2.223 2.223 0 0 1-3.075 2.11 2.22 2.22 0 0 1-1.312-1.568L.33 10.333Z" />
50
- <path d="M4.868 12.683a1.715 1.715 0 0 0 1.318-3.165 1.7 1.7 0 0 0-1.263-.02l1.023.424a1.261 1.261 0 1 1-.97 2.33l-.99-.41a1.7 1.7 0 0 0 .882.84Zm3.726-6.687a2.03 2.03 0 0 0 2.027 2.029 2.03 2.03 0 0 0 2.027-2.029 2.03 2.03 0 0 0-2.027-2.027 2.03 2.03 0 0 0-2.027 2.027m2.03-1.527a1.524 1.524 0 1 1-.002 3.048 1.524 1.524 0 0 1 .002-3.048" />
51
- </svg>
52
- `,
53
- code: `
54
- <svg
55
- aria-hidden="true"
56
- data-name="Layer 1"
57
- fill="currentColor"
58
- viewBox="0 0 52 52"
59
- xmlns="http://www.w3.org/2000/svg"
60
- >
61
- <path
62
- d="M8.55,36.91A6.55,6.55,0,1,1,2,43.45,6.54,6.54,0,0,1,8.55,36.91Zm17.45,0a6.55,6.55,0,1,1-6.55,6.54A6.55,6.55,0,0,1,26,36.91Zm17.45,0a6.55,6.55,0,1,1-6.54,6.54A6.54,6.54,0,0,1,43.45,36.91ZM8.55,19.45A6.55,6.55,0,1,1,2,26,6.55,6.55,0,0,1,8.55,19.45Zm17.45,0A6.55,6.55,0,1,1,19.45,26,6.56,6.56,0,0,1,26,19.45Zm17.45,0A6.55,6.55,0,1,1,36.91,26,6.55,6.55,0,0,1,43.45,19.45ZM8.55,2A6.55,6.55,0,1,1,2,8.55,6.54,6.54,0,0,1,8.55,2ZM26,2a6.55,6.55,0,1,1-6.55,6.55A6.55,6.55,0,0,1,26,2ZM43.45,2a6.55,6.55,0,1,1-6.54,6.55A6.55,6.55,0,0,1,43.45,2Z"
63
- fill-rule="evenodd"
64
- />
65
- </svg>
66
- `,
67
- password: `
68
- <svg
69
- aria-hidden="true"
70
- fill="currentColor"
71
- viewBox="0 0 24 24"
72
- xmlns="http://www.w3.org/2000/svg"
73
- >
74
- <path
75
- clip-rule="evenodd"
76
- d="M12 1.5a5.25 5.25 0 0 0-5.25 5.25v3a3 3 0 0 0-3 3v6.75a3 3 0 0 0 3 3h10.5a3 3 0 0 0 3-3v-6.75a3 3 0 0 0-3-3v-3c0-2.9-2.35-5.25-5.25-5.25Zm3.75 8.25v-3a3.75 3.75 0 1 0-7.5 0v3h7.5Z"
77
- fill-rule="evenodd"
78
- />
79
- </svg>
80
- `,
81
- twitch: `
82
- <svg
83
- aria-hidden="true"
84
- role="img"
85
- viewBox="0 0 448 512"
86
- xmlns="http://www.w3.org/2000/svg"
87
- >
88
- <path
89
- d="M40.1 32L10 108.9v314.3h107V480h60.2l56.8-56.8h87l117-117V32H40.1zm357.8 254.1L331 353H224l-56.8 56.8V353H76.9V72.1h321v214zM331 149v116.9h-40.1V149H331zm-107 0v116.9h-40.1V149H224z"
90
- fill="currentColor"
91
- />
92
- </svg>
93
- `,
94
- google: ICON_GOOGLE,
95
- github: ICON_GITHUB,
96
- apple: `
97
- <svg
98
- aria-hidden="true"
99
- role="img"
100
- viewBox="0 0 814 1000"
101
- xmlns="http://www.w3.org/2000/svg"
102
- >
103
- <path
104
- d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z "
105
- fill="currentColor"
106
- />
107
- </svg>
108
- `,
109
- x: `
110
- <svg
111
- aria-hidden="true"
112
- role="img"
113
- viewBox="0 0 1200 1227"
114
- xmlns="http://www.w3.org/2000/svg"
115
- >
116
- <path
117
- d="M714.163 519.284 1160.89 0h-105.86L667.137 450.887 357.328 0H0l468.492 681.821L0 1226.37h105.866l409.625-476.152 327.181 476.152H1200L714.137 519.284h.026ZM569.165 687.828l-47.468-67.894-377.686-540.24h162.604l304.797 435.991 47.468 67.894 396.2 566.721H892.476L569.165 687.854v-.026Z"
118
- fill="currentColor"
119
- />
120
- </svg>
121
- `,
122
- microsoft: `
123
- <svg
124
- aria-hidden="true"
125
- preserveAspectRatio="xMidYMid"
126
- role="img"
127
- viewBox="0 0 256 256"
128
- xmlns="http://www.w3.org/2000/svg"
129
- >
130
- <path d="M121.666 121.666H0V0h121.666z" fill="#F1511B" />
131
- <path d="M256 121.666H134.335V0H256z" fill="#80CC28" />
132
- <path d="M121.663 256.002H0V134.336h121.663z" fill="#00ADEF" />
133
- <path d="M256 256.002H134.335V134.336H256z" fill="#FBBC09" />
134
- </svg>
135
- `,
136
- facebook: `
137
- <svg
138
- aria-hidden="true"
139
- fill="url(#a)"
140
- role="img"
141
- viewBox="0 0 36 36"
142
- xmlns="http://www.w3.org/2000/svg"
143
- >
144
- <defs>
145
- <linearGradient id="a" x1="50%" x2="50%" y1="97.078%" y2="0%">
146
- <stop offset="0%" stop-color="#0062E0" />
147
- <stop offset="100%" stop-color="#19AFFF" />
148
- </linearGradient>
149
- </defs>
150
- <path d="M15 35.8C6.5 34.3 0 26.9 0 18 0 8.1 8.1 0 18 0s18 8.1 18 18c0 8.9-6.5 16.3-15 17.8l-1-.8h-4l-1 .8z" />
151
- <path
152
- d="m25 23 .8-5H21v-3.5c0-1.4.5-2.5 2.7-2.5H26V7.4c-1.3-.2-2.7-.4-4-.4-4.1 0-7 2.5-7 7v4h-4.5v5H15v12.7c1 .2 2 .3 3 .3s2-.1 3-.3V23h4z"
153
- fill="#FFF"
154
- />
155
- </svg>
156
- `,
157
- slack: `
158
- <svg
159
- aria-hidden="true"
160
- enable-background="new 0 0 2447.6 2452.5"
161
- role="img"
162
- viewBox="0 0 2447.6 2452.5"
163
- xmlns="http://www.w3.org/2000/svg"
164
- >
165
- <g clip-rule="evenodd" fill-rule="evenodd">
166
- <path
167
- d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z"
168
- fill="#36c5f0"
169
- />
170
- <path
171
- d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z"
172
- fill="#2eb67d"
173
- />
174
- <path
175
- d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z"
176
- fill="#ecb22e"
177
- />
178
- <path
179
- d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0"
180
- fill="#e01e5a"
181
- />
182
- </g>
183
- </svg>
184
- `
34
+ const ProviderButton = ({ providerKey, providerType, config, displays, buttonText }) => {
35
+ const displayName = config?.display || displays[providerType] || DEFAULT_DISPLAYS[providerType] || providerType;
36
+ const IconComponent = PROVIDER_ICONS[providerKey] || PROVIDER_ICONS[providerType];
37
+ return /* @__PURE__ */ jsxs("a", {
38
+ href: `./${providerKey}/authorize`,
39
+ "data-component": "button",
40
+ "data-color": "ghost",
41
+ "aria-label": `${buttonText} ${displayName}`,
42
+ children: [
43
+ IconComponent && /* @__PURE__ */ jsx("i", {
44
+ "data-slot": "icon",
45
+ children: /* @__PURE__ */ jsx(IconComponent, {})
46
+ }),
47
+ buttonText,
48
+ " ",
49
+ displayName
50
+ ]
51
+ });
52
+ };
53
+ /**
54
+ * Main provider selection component
55
+ */
56
+ const ProviderSelect = ({ providers, config = {}, theme }) => {
57
+ const buttonText = config.copy?.button_provider || "Continue with";
58
+ const displays = {
59
+ ...DEFAULT_DISPLAYS,
60
+ ...config.displays
61
+ };
62
+ const visibleProviders = Object.entries(providers).filter(([key]) => !config.providers?.[key]?.hide);
63
+ return /* @__PURE__ */ jsx(Layout, {
64
+ theme,
65
+ title: "Sign In",
66
+ children: /* @__PURE__ */ jsx("div", {
67
+ "data-component": "form",
68
+ children: visibleProviders.map(([key, type]) => /* @__PURE__ */ jsx(ProviderButton, {
69
+ providerKey: key,
70
+ providerType: type,
71
+ config: config.providers?.[key],
72
+ displays,
73
+ buttonText
74
+ }, key))
75
+ })
76
+ });
185
77
  };
186
78
  /**
187
- * Creates a provider selection UI component for OAuth authentication.
188
- *
189
- * This component generates a complete authentication provider selection interface that:
190
- * - Displays available OAuth providers as clickable buttons
191
- * - Includes appropriate icons for recognized providers
192
- * - Supports custom theming and internationalization
193
- * - Provides accessible markup with proper ARIA attributes
194
- * - Handles provider visibility and custom display names
195
- *
196
- * @param props - Configuration options for customizing the select UI
197
- * @returns An async function that generates the HTML response for the selection interface
198
- *
199
- * @example
200
- * ```ts
201
- * // Basic usage with defaults
202
- * const selectUI = Select()
203
- *
204
- * // Customized with provider configuration
205
- * const selectUI = Select({
206
- * copy: {
207
- * button_provider: "Sign in with"
208
- * },
209
- * displays: {
210
- * github: "GitHub Account",
211
- * code: "Email Code"
212
- * },
213
- * providers: {
214
- * facebook: { hide: true },
215
- * google: { display: "Google Workspace" }
216
- * }
217
- * })
218
- *
219
- * // Use in issuer configuration
220
- * export default issuer({
221
- * select: selectUI,
222
- * // ... other configuration
223
- * })
224
- * ```
225
- *
226
- * ## Provider Resolution Logic
227
- *
228
- * Display names are resolved in the following priority order:
229
- * 1. Provider-specific `display` property
230
- * 2. Global `displays` type override
231
- * 3. Default display name from `DEFAULT_DISPLAY`
232
- * 4. Provider type string as final fallback
233
- *
234
- * ## Accessibility Features
235
- *
236
- * The generated UI includes:
237
- * - Semantic HTML structure
238
- * - Proper button roles and keyboard navigation
239
- * - Icon accessibility with `aria-hidden="true"`
240
- * - Screen reader friendly provider names
79
+ * Creates a provider selection UI
241
80
  */
242
- const Select = (props) => {
243
- return async (providers, _req) => {
244
- const copy = {
245
- ...DEFAULT_COPY,
246
- ...props?.copy
247
- };
248
- const displays = {
249
- ...DEFAULT_DISPLAY,
250
- ...props?.displays
251
- };
252
- const providerButtons = Object.entries(providers).map(([providerKey, providerType]) => {
253
- const providerConfig = props?.providers?.[providerKey];
254
- if (providerConfig?.hide) return "";
255
- const displayName = providerConfig?.display || displays[providerType] || providerType;
256
- const icon = ICON[providerKey];
257
- return `
258
- <a
259
- aria-label="${copy.button_provider} ${displayName}"
260
- data-color="ghost"
261
- data-component="button"
262
- href="./${providerKey}/authorize"
263
- >
264
- ${icon ? `<i data-slot="icon">${icon}</i>` : ""}
265
- ${copy.button_provider} ${displayName}
266
- </a>
267
- `;
268
- }).filter((button) => button !== "").join("");
269
- const formContent = `
270
- <div data-component="form">
271
- ${providerButtons}
272
- </div>
273
- `;
274
- const html = Layout({ children: formContent });
81
+ const Select = (props = {}) => {
82
+ return async (providers) => {
83
+ const html = renderToHTML(/* @__PURE__ */ jsx(ProviderSelect, {
84
+ providers,
85
+ config: props,
86
+ theme: props.theme
87
+ }));
275
88
  return new Response(html, { headers: { "Content-Type": "text/html" } });
276
89
  };
277
90
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "devDependencies": {
40
- "@types/node": "^24.0.12",
40
+ "@types/node": "^24.0.13",
41
41
  "tsdown": "^0.12.9",
42
42
  "typescript": "^5.8.3",
43
43
  "@draftlab/tsconfig": "0.1.0"
@@ -57,6 +57,8 @@
57
57
  "dependencies": {
58
58
  "@standard-schema/spec": "^1.0.0",
59
59
  "jose": "^6.0.11",
60
+ "preact": "^10.26.9",
61
+ "preact-render-to-string": "^6.5.13",
60
62
  "@draftlab/auth-router": "0.0.2"
61
63
  },
62
64
  "engines": {