-
+
+ {roomUrl ? (
+
+ ) : undefined
+ }
+ >
+ {isCopied ? (
+ th('laterMeetingDialog.copied')
+ ) : (
+
+ )}
+
+ ) : (
+
}
+ >
+ {t('label')}
+
+ )}
)
}
diff --git a/src/frontend/src/primitives/Button.tsx b/src/frontend/src/primitives/Button.tsx
index 8ee63d03..1724edd1 100644
--- a/src/frontend/src/primitives/Button.tsx
+++ b/src/frontend/src/primitives/Button.tsx
@@ -6,12 +6,15 @@ import { type RecipeVariantProps } from '@/styled-system/css'
import { buttonRecipe, type ButtonRecipe } from './buttonRecipe'
import { TooltipWrapper, type TooltipWrapperProps } from './TooltipWrapper'
import { ReactNode } from 'react'
+import { Loader } from './Loader'
export type ButtonProps = RecipeVariantProps
&
RACButtonsProps &
TooltipWrapperProps & {
// Use tooltip as description below the button.
description?: boolean
+ } & {
+ icon?: ReactNode
}
export const Button = ({
@@ -27,6 +30,8 @@ export const Button = ({
className={buttonRecipe(variantProps)}
{...(componentProps as RACButtonsProps)}
>
+ {!props.loading && props.icon}
+ {props.loading && }
{componentProps.children as ReactNode}
{props.description && {tooltip}}
diff --git a/src/frontend/src/primitives/Loader.tsx b/src/frontend/src/primitives/Loader.tsx
new file mode 100644
index 00000000..272fc380
--- /dev/null
+++ b/src/frontend/src/primitives/Loader.tsx
@@ -0,0 +1,45 @@
+import { cva } from '@/styled-system/css'
+
+const loader = cva({
+ base: {
+ borderRadius: '50%',
+ position: 'relative',
+ animation: 'rotate 1s linear infinite',
+ '&:before, &:after': {
+ content: '""',
+ boxSizing: 'border-box',
+ position: 'absolute',
+ inset: '0',
+ borderRadius: '50%',
+ borderStyle: 'solid',
+ borderColor: 'white',
+ },
+ _before: {
+ animation: 'prixClipFix 2s linear infinite',
+ },
+ _after: {
+ borderColor: 'white',
+ animation:
+ 'prixClipFix 2s linear infinite, rotate 0.5s linear infinite reverse',
+ inset: 6,
+ },
+ },
+ variants: {
+ size: {
+ small: {
+ width: '24px',
+ height: '24px',
+ '&:before, &:after': {
+ borderWidth: '2px',
+ },
+ },
+ },
+ },
+ defaultVariants: {
+ size: 'small',
+ },
+})
+
+export const Loader = () => {
+ return
+}
diff --git a/src/frontend/src/primitives/buttonRecipe.ts b/src/frontend/src/primitives/buttonRecipe.ts
index 7f294980..aea4deba 100644
--- a/src/frontend/src/primitives/buttonRecipe.ts
+++ b/src/frontend/src/primitives/buttonRecipe.ts
@@ -8,6 +8,7 @@ export const buttonRecipe = cva({
transition: 'background 200ms, outline 200ms, border-color 200ms',
cursor: 'pointer',
border: '1px solid transparent',
+ gap: '0.5rem',
},
variants: {
size: {
@@ -229,6 +230,9 @@ export const buttonRecipe = cva({
width: 'full',
},
},
+ loading: {
+ true: {},
+ },
// some toggle buttons make more sense without a "pushed button" style when selected because their content changes to mark the state
shySelected: {
true: {},
diff --git a/src/frontend/src/routes.ts b/src/frontend/src/routes.ts
index a8c0bbd1..e50d2665 100644
--- a/src/frontend/src/routes.ts
+++ b/src/frontend/src/routes.ts
@@ -28,11 +28,11 @@ export const routes: Record<
path: '/feedback',
Component: FeedbackRoute,
},
- sdkCreateButton: {
- name: 'sdkCreateButton',
- path: '/sdk/create-button',
- Component: SdkCreateButton,
- },
+ // sdkCreateButton: {
+ // name: 'sdkCreateButton',
+ // path: '/sdk/create-button',
+ // Component: SdkCreateButton,
+ // },
}
export type RouteName = keyof typeof routes
diff --git a/src/frontend/src/styles/index.css b/src/frontend/src/styles/index.css
index 1bc20436..a821a5cd 100644
--- a/src/frontend/src/styles/index.css
+++ b/src/frontend/src/styles/index.css
@@ -38,3 +38,34 @@ body:has(.lk-video-conference) #crisp-chatbox > div > a {
transform: translateY(0);
}
}
+
+/**
+ * Loader animations.
+ */
+
+@keyframes rotate {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes prixClipFix {
+ 0% {
+ clip-path: polygon(50% 50%, 0 0, 0 0, 0 0, 0 0, 0 0);
+ }
+ 25% {
+ clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 0, 100% 0, 100% 0);
+ }
+ 50% {
+ clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 100% 100%, 100% 100%);
+ }
+ 75% {
+ clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 100%);
+ }
+ 100% {
+ clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 0);
+ }
+}