@olympusoss/canvas 2.12.0 → 2.13.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olympusoss/canvas",
3
- "version": "2.12.0",
3
+ "version": "2.13.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../../lib/utils";
4
+
5
+ export interface OrSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
6
+ /**
7
+ * Text rendered between the two rule segments.
8
+ * @default "or"
9
+ */
10
+ label?: React.ReactNode;
11
+ }
12
+
13
+ /**
14
+ * Horizontal divider with a centred label, e.g. between social-provider
15
+ * buttons and an email/password form. Two `border-t` rules flank a short
16
+ * upper-case label sitting on the card background.
17
+ *
18
+ * Place inside a card whose background is `bg-card`; the label inherits
19
+ * that surface so the rules visually meet behind it.
20
+ */
21
+ const OrSeparator = React.forwardRef<HTMLDivElement, OrSeparatorProps>(
22
+ ({ label = "or", className, ...props }, ref) => (
23
+ <div
24
+ ref={ref}
25
+ role="separator"
26
+ aria-orientation="horizontal"
27
+ className={cn("flex items-center gap-3 py-1", className)}
28
+ {...props}
29
+ >
30
+ <div className="h-px flex-1 bg-border" />
31
+ <span className="text-[11px] uppercase tracking-[0.08em] text-muted-foreground">{label}</span>
32
+ <div className="h-px flex-1 bg-border" />
33
+ </div>
34
+ ),
35
+ );
36
+ OrSeparator.displayName = "OrSeparator";
37
+
38
+ export { OrSeparator };
@@ -0,0 +1,165 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+ import { Button } from "../atoms/button";
7
+
8
+ /* ──────────────────────────────────────────────────────────────────
9
+ Brand glyphs. Tiny, currentColor for monochrome marks
10
+ (GitHub, Microsoft outline, generic SSO). Multi-color marks
11
+ (Google, Apple) inline their official palette.
12
+ ────────────────────────────────────────────────────────────────── */
13
+
14
+ const GitHubGlyph = () => (
15
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
16
+ <title>GitHub</title>
17
+ <path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2c-3.2.69-3.87-1.54-3.87-1.54-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.75 1.18 1.75 1.18 1.02 1.75 2.68 1.24 3.34.95.1-.74.4-1.24.72-1.52-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.28 1.18-3.08-.12-.29-.51-1.46.11-3.04 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.78 0c2.21-1.49 3.18-1.18 3.18-1.18.62 1.58.23 2.75.11 3.04.74.8 1.18 1.82 1.18 3.08 0 4.42-2.69 5.39-5.26 5.68.41.35.77 1.04.77 2.11v3.13c0 .31.21.67.79.56A11.51 11.51 0 0 0 23.5 12c0-6.35-5.15-11.5-11.5-11.5z" />
18
+ </svg>
19
+ );
20
+
21
+ const GoogleGlyph = () => (
22
+ <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden>
23
+ <title>Google</title>
24
+ <path
25
+ fill="#4285F4"
26
+ d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.76h3.56c2.08-1.92 3.28-4.74 3.28-8.09z"
27
+ />
28
+ <path
29
+ fill="#34A853"
30
+ d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.56-2.76c-.98.66-2.24 1.06-3.72 1.06-2.86 0-5.29-1.93-6.15-4.53H2.18v2.84A11 11 0 0 0 12 23z"
31
+ />
32
+ <path
33
+ fill="#FBBC05"
34
+ d="M5.85 14.11A6.6 6.6 0 0 1 5.5 12c0-.73.13-1.44.35-2.11V7.05H2.18A11 11 0 0 0 1 12c0 1.78.43 3.46 1.18 4.95l3.67-2.84z"
35
+ />
36
+ <path
37
+ fill="#EA4335"
38
+ d="M12 5.38c1.62 0 3.06.56 4.2 1.64l3.15-3.15C17.45 2.1 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.05l3.67 2.84C6.71 7.31 9.14 5.38 12 5.38z"
39
+ />
40
+ </svg>
41
+ );
42
+
43
+ const AppleGlyph = () => (
44
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
45
+ <title>Apple</title>
46
+ <path d="M17.05 12.04c-.03-2.85 2.32-4.22 2.43-4.29-1.32-1.94-3.39-2.21-4.12-2.24-1.75-.18-3.42 1.03-4.31 1.03-.9 0-2.27-1.01-3.74-.98-1.92.03-3.7 1.12-4.69 2.83-2.01 3.47-.51 8.6 1.43 11.43.96 1.38 2.09 2.93 3.56 2.87 1.44-.06 1.98-.92 3.71-.92 1.73 0 2.22.92 3.74.89 1.54-.03 2.52-1.4 3.46-2.79 1.1-1.6 1.55-3.15 1.58-3.23-.03-.01-3.03-1.16-3.05-4.6zM14.27 3.65c.79-.96 1.32-2.29 1.18-3.62-1.14.05-2.52.76-3.34 1.71-.73.84-1.37 2.19-1.2 3.49 1.27.1 2.57-.65 3.36-1.58z" />
47
+ </svg>
48
+ );
49
+
50
+ const MicrosoftGlyph = () => (
51
+ <svg width="16" height="16" viewBox="0 0 24 24" aria-hidden>
52
+ <title>Microsoft</title>
53
+ <path fill="#F25022" d="M1 1h10v10H1z" />
54
+ <path fill="#7FBA00" d="M13 1h10v10H13z" />
55
+ <path fill="#00A4EF" d="M1 13h10v10H1z" />
56
+ <path fill="#FFB900" d="M13 13h10v10H13z" />
57
+ </svg>
58
+ );
59
+
60
+ const SsoGlyph = () => (
61
+ <svg
62
+ width="16"
63
+ height="16"
64
+ viewBox="0 0 24 24"
65
+ fill="none"
66
+ stroke="currentColor"
67
+ strokeWidth={2}
68
+ strokeLinecap="round"
69
+ strokeLinejoin="round"
70
+ aria-hidden
71
+ >
72
+ <title>SSO</title>
73
+ <rect x="3" y="11" width="18" height="11" rx="2" />
74
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
75
+ </svg>
76
+ );
77
+
78
+ /**
79
+ * Known social/SSO providers. Each entry pairs a glyph with a default
80
+ * label; the caller can override the label per-button if needed.
81
+ */
82
+ export type SocialProvider = "github" | "google" | "apple" | "microsoft" | "sso";
83
+
84
+ interface ProviderMeta {
85
+ glyph: React.ReactNode;
86
+ label: string;
87
+ }
88
+
89
+ const PROVIDERS: Record<SocialProvider, ProviderMeta> = {
90
+ github: { glyph: <GitHubGlyph />, label: "Continue with GitHub" },
91
+ google: { glyph: <GoogleGlyph />, label: "Continue with Google" },
92
+ apple: { glyph: <AppleGlyph />, label: "Continue with Apple" },
93
+ microsoft: { glyph: <MicrosoftGlyph />, label: "Continue with Microsoft" },
94
+ sso: { glyph: <SsoGlyph />, label: "Continue with SSO" },
95
+ };
96
+
97
+ export interface SocialButtonProps
98
+ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
99
+ /** Provider identifier. Picks the glyph and default label. */
100
+ provider: SocialProvider;
101
+ /** Override the default label (e.g. "Continue with Okta"). */
102
+ label?: string;
103
+ }
104
+
105
+ /**
106
+ * Single provider button. Outline variant with the provider glyph on the
107
+ * leading edge. Use directly when you want one provider, or compose
108
+ * several inside `SocialButtons`.
109
+ */
110
+ const SocialButton = React.forwardRef<HTMLButtonElement, SocialButtonProps>(
111
+ ({ provider, label, className, ...props }, ref) => {
112
+ const meta = PROVIDERS[provider];
113
+ return (
114
+ <Button
115
+ ref={ref}
116
+ type="button"
117
+ variant="outline"
118
+ className={cn("w-full justify-center gap-2", className)}
119
+ {...props}
120
+ >
121
+ {meta.glyph}
122
+ <span>{label ?? meta.label}</span>
123
+ </Button>
124
+ );
125
+ },
126
+ );
127
+ SocialButton.displayName = "SocialButton";
128
+
129
+ export interface SocialButtonsProps extends React.HTMLAttributes<HTMLDivElement> {
130
+ /**
131
+ * Provider list rendered vertically. Pass `["github", "google"]` for the
132
+ * common CIAM case, or `["sso"]` for a single corporate SSO button.
133
+ * Defaults to `["github", "google"]`.
134
+ */
135
+ providers?: SocialProvider[];
136
+ /** Per-provider click handler. Receives the provider id. */
137
+ onProviderClick?: (provider: SocialProvider) => void;
138
+ /** When true, all buttons render disabled (e.g. while signing in). */
139
+ disabled?: boolean;
140
+ }
141
+
142
+ /**
143
+ * Vertical stack of social/SSO provider buttons. Typically placed at the
144
+ * top of a sign-in or sign-up card, above an `OrSeparator`.
145
+ *
146
+ * Stays purely presentational: the consumer wires `onProviderClick` to
147
+ * the OAuth2 initiation flow.
148
+ */
149
+ const SocialButtons = React.forwardRef<HTMLDivElement, SocialButtonsProps>(
150
+ ({ providers = ["github", "google"], onProviderClick, disabled, className, ...props }, ref) => (
151
+ <div ref={ref} className={cn("flex flex-col gap-2.5", className)} {...props}>
152
+ {providers.map((p) => (
153
+ <SocialButton
154
+ key={p}
155
+ provider={p}
156
+ disabled={disabled}
157
+ onClick={() => onProviderClick?.(p)}
158
+ />
159
+ ))}
160
+ </div>
161
+ ),
162
+ );
163
+ SocialButtons.displayName = "SocialButtons";
164
+
165
+ export { SocialButton, SocialButtons };
package/src/index.ts CHANGED
@@ -230,6 +230,10 @@ export {
230
230
  NumberBadge,
231
231
  type NumberBadgeProps,
232
232
  } from "./components/molecules/number-badge";
233
+ export {
234
+ OrSeparator,
235
+ type OrSeparatorProps,
236
+ } from "./components/molecules/or-separator";
233
237
  export {
234
238
  PageHeader,
235
239
  type PageHeaderBreadcrumb,
@@ -274,6 +278,13 @@ export {
274
278
  SectionCard,
275
279
  type SectionCardProps,
276
280
  } from "./components/molecules/section-card";
281
+ export {
282
+ SocialButton,
283
+ type SocialButtonProps,
284
+ SocialButtons,
285
+ type SocialButtonsProps,
286
+ type SocialProvider,
287
+ } from "./components/molecules/social-buttons";
277
288
  export { StatCard, type StatCardProps } from "./components/molecules/stat-card";
278
289
  export {
279
290
  StatusBadge,