Skip to content

Commit

Permalink
feat: update settings cards and button styles
Browse files Browse the repository at this point in the history
  • Loading branch information
mszekiel committed Sep 11, 2024
1 parent 47d221a commit 63a61bb
Show file tree
Hide file tree
Showing 16 changed files with 237 additions and 40 deletions.
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/elements-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"types": "./dist/index.d.ts",
"dependencies": {
"@ory/client-fetch": "1.15.0-next.0",
"class-variance-authority": "^0.7.0",
"clsx": "2.1.1",
"input-otp": "1.2.4",
"react-hook-form": "7.52.1",
Expand Down
6 changes: 6 additions & 0 deletions packages/elements-react/src/components/form/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
HeadlessAuthMethodListItemProps,
HeadlessButtonProps,
HeadlessFormProps,
HeadlessFormSectionContentProps,
HeadlessFormSectionProps,
HeadlessImageProps,
HeadlessInputProps,
HeadlessLabelProps,
Expand Down Expand Up @@ -71,6 +73,10 @@ export type OryFormComponents = {
MessageContainer: ComponentType<HeadlessMessagesProps>
Message: ComponentType<HeadlessMessageProps>
CurrentIdentifierButton: ComponentType<HeadlessButtonProps>

FormSection: ComponentType<HeadlessFormSectionProps>
FormSectionContent: ComponentType<HeadlessFormSectionContentProps>
FormSectionFooter: ComponentType<HeadlessFormSectionContentProps>
}

export type OryFormProps = PropsWithChildren
Expand Down
16 changes: 16 additions & 0 deletions packages/elements-react/src/components/form/section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useComponents } from "../../context/component"
import { PropsWithChildren } from "react"
import { HeadlessFormSectionProps } from "../../types"
import { OryForm } from "./form"

export type HeadlessGroupContainerProps = PropsWithChildren

export function OryFormSection({ children }: HeadlessFormSectionProps) {
const { FormSection } = useComponents()

return (
<OryForm>
<FormSection>{children}</FormSection>
</OryForm>
)
}
1 change: 1 addition & 0 deletions packages/elements-react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
export * from "./card"
export * from "./form"
export * from "./generic"
export * from "./settings"
1 change: 1 addition & 0 deletions packages/elements-react/src/components/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./settings-card"
48 changes: 48 additions & 0 deletions packages/elements-react/src/components/settings/settings-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { UiNodeGroupEnum } from "@ory/client-fetch"
import { useComponents, useOryFlow } from "../../context"
import { useNodesGroups } from "../../util/ui"
import { Node } from "../form/nodes/node"
import { OryFormSection } from "../form/section"

export function OrySettingsCard() {
const Components = useComponents()
const { flowType, flow } = useOryFlow()
const uniqueGroups = useNodesGroups(flow.ui.nodes)

return (
<div className="flex flex-col gap-8 max-w-[848px]">
{Object.entries(uniqueGroups).map(([group, nodes]) => {
if (group === UiNodeGroupEnum.Default) {
return null
}

return (
<OryFormSection key={group} title={group} description={group}>
<Components.FormSectionContent>
{nodes
.filter(
(node) =>
"type" in node.attributes &&
node.attributes.type !== "submit",
)
.map((node, k) => (
<Node key={k} node={node} />
))}
</Components.FormSectionContent>
<Components.FormSectionFooter>
{nodes
.filter(
(node) =>
"type" in node.attributes &&
node.attributes.type === "submit",
)
.map((node, k) => (
<Node key={k} node={node} />
))}
</Components.FormSectionFooter>
</OryFormSection>
)
})}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ import {
import { DefaultText } from "./form/text"
import { DefaultCurrentIdentifierButton } from "./card/current-identifier-button"
import { OryFlowComponents } from "../../../types"
import {
DefaultFormSection,
DefaultFormSectionContent,
DefaultFormSectionFooter,
} from "./form/section"

export const OryDefaultComponents: OryFlowComponents = {
Card: DefaultCard,
Expand Down Expand Up @@ -57,4 +62,8 @@ export const OryDefaultComponents: OryFlowComponents = {

AuthMethodListItem: DefaultAuthMethodListItem,
CurrentIdentifierButton: DefaultCurrentIdentifierButton,

FormSection: DefaultFormSection,
FormSectionContent: DefaultFormSectionContent,
FormSectionFooter: DefaultFormSectionFooter,
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,49 @@
import { useFormContext } from "react-hook-form"
import { useIntl } from "react-intl"
import { Spinner } from "./spinner"
import { cn } from "../../utils/cn"
import { HeadlessButtonProps } from "../../../../types"
import { formatMessage } from "../../../../util"
import { getNodeLabel } from "@ory/client-fetch"
import { FlowType, getNodeLabel } from "@ory/client-fetch"
import { cva, VariantProps } from "class-variance-authority"
import { useOryFlow } from "../../../../context"

const buttonStyles = cva(
[
"ring-1 relative overflow-hidden ring-inset rounded text-sm leading-none flex gap-3 justify-center",
"disabled:cursor-not-allowed loading:before:pointer-events-none loading:cursor-wait",
"transition-colors ease-linear duration-100",
],
{
variants: {
intent: {
primary: [
"bg-button-primary-bg-default text-button-primary-fg-default ring-button-primary-border-default",
"hover:bg-button-primary-bg-hover hover:text-button-primary-fg-hover hover:ring-button-primary-border-hover",
"disabled:bg-button-primary-bg-disabled disabled:text-button-primary-fg-disabled disabled:ring-button-primary-border-disabled",
"loading:bg-button-primary-bg-default loading:text-button-primary-fg-default loading:ring-button-primary-border-default",
"loading:before:absolute loading:before:content-[''] loading:before:inset-0 loading:before:bg-button-primary-bg-default loading:before:opacity-80",
],
secondary: [
"bg-button-secondary-bg-default text-button-secondary-fg-default ring-button-secondary-border-default",
"hover:bg-button-secondary-bg-hover hover:text-button-secondary-fg-hover hover:ring-button-secondary-border-hover",
"disabled:bg-button-secondary-bg-disabled disabled:text-button-secondary-fg-disabled disabled:ring-button-secondary-border-disabled",
"loading:bg-button-secondary-bg-default loading:text-button-secondary-fg-default loading:ring-button-secondary-border-default",
"loading:before:absolute loading:before:content-[''] loading:before:inset-0 loading:before:bg-button-secondary-bg-default loading:before:opacity-80",
],
},
size: {
default: ["px-4 py-3"],
large: ["px-4 py-4.5"],
},
defaultVariants: {
intent: "primary",
size: "default",
},
},
},
)

export type ButtonVariants = VariantProps<typeof buttonStyles>

export const DefaultButton = ({
attributes,
Expand All @@ -24,6 +63,7 @@ export const DefaultButton = ({
} = attributes
const intl = useIntl()
const label = getNodeLabel(node)
const { flowType } = useOryFlow()
const {
formState: { isSubmitting },
setValue,
Expand All @@ -34,6 +74,8 @@ export const DefaultButton = ({
attributes.name.includes("passkey") ||
attributes.name.includes("webauthn")

const isSmall = flowType === FlowType.Settings

return (
<button
{...rest}
Expand All @@ -44,42 +86,20 @@ export const DefaultButton = ({
setValue(name, value)
}}
onClick={(e) => {
if (onClick) {
onClick(e)
}

onClick?.(e)
if (type !== "button") {
setValue(name, value)
}
}}
className={cn(
// TODO: Difficult to resolve merge conflict here. Please ensure this is correct:
// "relative antialiased rounded border gap-3 leading-none transition-colors ease-linear duration-100 px-4 py-4.5 text-sm font-medium",
"antialiased rounded-border-radius-buttons border border-transparent gap-3 bg-button-primary-bg-default hover:bg-button-primary-bg-hover transition-colors text-button-primary-fg-default hover:text-button-primary-fg-hover px-4 py-4.5 text-sm leading-none font-medium",
{
"cursor-not-allowed": isSubmitting,
"bg-button-primary-bg-hover": isSubmitting,
"bg-button-primary-bg-default hover:bg-button-primary-bg-hover text-button-primary-fg-default hover:text-button-primary-fg-hover":
isPrimary,
"bg-button-secondary-bg-default hover:bg-button-secondary-bg-hover text-button-secondary-fg-default hover:text-button-secondary-fg-hover border-button-secondary-border-default":
!isPrimary,
},
{},
)}
className={buttonStyles({
intent: isPrimary ? "primary" : "secondary",
size: isSmall ? "default" : "large",
})}
disabled={rest.disabled ?? isSubmitting}
data-loading={isSubmitting}
>
{isSubmitting ? <Spinner /> : null}
<span
className={cn(
"transition-colors ease-linear duration-100 leading-none text-button-primary-fg-default/20",
{
"text-button-primary-fg-default opacity-20 transition-opacity":
isSubmitting && isPrimary,
"text-button-secondary-fg-default/20": isSubmitting && !isPrimary,
},
)}
>
{formatMessage(label, intl)}
</span>
{formatMessage(label, intl)}
</button>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
HeadlessFormSectionContentProps,
HeadlessFormSectionFooterProps,
HeadlessFormSectionProps,
} from "../../../../types"

const DefaultFormSection = ({ children }: HeadlessFormSectionProps) => {
return <div className="flex flex-col">{children}</div>
}

const DefaultFormSectionContent = ({
children,
}: HeadlessFormSectionContentProps) => {
return (
<div className="border rounded-t-xl border-b-0 border-dialog-border-default bg-forms-bg-default px-6 py-8 flex flex-col gap-8">
{children}
</div>
)
}

const DefaultFormSectionFooter = ({
children,
}: HeadlessFormSectionFooterProps) => {
return (
<div className="rounded-b-xl flex justify-end px-6 py-4 bg-dialog-bg-subtle border border-dialog-border-default">
{children}
</div>
)
}

export {
DefaultFormSection,
DefaultFormSectionContent,
DefaultFormSectionFooter,
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function Spinner({ className }: { className?: string }) {
aria-hidden="true"
role="status"
className={cn(
"absolute inset-0 mx-auto my-auto w-8 h-8 text-branding-fg-default animate-spin text-button-primary-fg-default",
"absolute pointer-events-none inset-0 mx-auto my-auto w-8 h-8 animate-spin",
className,
)}
viewBox="0 0 34 34"
Expand Down
7 changes: 5 additions & 2 deletions packages/elements-react/src/theme/default/flows/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { PropsWithChildren } from "react"
import { OryDefaultComponents } from "../components"
import { OryFlowComponents } from "../../../types"
import { OryProvider } from "../../../context"
import { OryCard } from "../../../components"
import { OryClientConfiguration } from "../../../util/clientConfiguration"
import { OrySettingsCard } from "../../../components"

export type SettingsFlowContextProps = {
flow: SettingsFlow
Expand All @@ -23,14 +23,17 @@ export function Settings({
...OryDefaultComponents,
...flowOverrideComponents,
}

console.log(flow.ui)

return (
<OryProvider
config={config}
flow={flow}
flowType={FlowType.Settings}
components={components}
>
{children ?? <OryCard />}
{children ?? <OrySettingsCard />}
</OryProvider>
)
}
Loading

0 comments on commit 63a61bb

Please sign in to comment.