mirror of https://github.com/tailscale/tailscale/
client/web: style tweaks
Style changes made in live pairing session. Updates #10261 Co-authored-by: Will Norris <will@tailscale.com> Co-authored-by: Alessandro Mingione <alessandro@tailscale.com> Signed-off-by: Sonia Appasamy <sonia@tailscale.com>pull/10462/head
parent
1a4d423328
commit
014ae98297
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import cx from "classnames"
|
||||||
|
import React from "react"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
elevated?: boolean
|
||||||
|
empty?: boolean
|
||||||
|
noPadding?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card is a box with a border, rounded corners, and some padding. Use it to
|
||||||
|
* group content into a single container and give it more importance. The
|
||||||
|
* elevation prop gives it a box shadow, while the empty prop a light gray
|
||||||
|
* background color.
|
||||||
|
*
|
||||||
|
* <Card>{content}</Card>
|
||||||
|
* <Card elevated>{content}</Card>
|
||||||
|
* <Card empty><EmptyState description="You don't have any keys" /></Card>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default function Card(props: Props) {
|
||||||
|
const { children, className, elevated, empty, noPadding } = props
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx("rounded-md border", className, {
|
||||||
|
"shadow-soft": elevated,
|
||||||
|
"bg-gray-0": empty,
|
||||||
|
"bg-white": !empty,
|
||||||
|
"p-6": !noPadding,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import cx from "classnames"
|
||||||
|
import React, { cloneElement } from "react"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
action?: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
description: string
|
||||||
|
icon?: React.ReactElement
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmptyState shows some text and an optional action when some area that can
|
||||||
|
* house content is empty (eg. no search results, empty tables).
|
||||||
|
*/
|
||||||
|
export default function EmptyState(props: Props) {
|
||||||
|
const { action, className, description, icon, title } = props
|
||||||
|
const iconColor = "text-gray-500"
|
||||||
|
const iconComponent = getIcon(icon, iconColor)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx("flex justify-center", className, {
|
||||||
|
"flex-col items-center": action || icon || title,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{icon && <div className="mb-2">{iconComponent}</div>}
|
||||||
|
{title && (
|
||||||
|
<h3 className="text-xl font-medium text-center mb-2">{title}</h3>
|
||||||
|
)}
|
||||||
|
<div className="w-full text-center max-w-xl text-gray-500">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
{action && <div className="mt-3.5">{action}</div>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcon(icon: React.ReactElement | undefined, iconColor: string) {
|
||||||
|
return icon ? cloneElement(icon, { className: iconColor }) : null
|
||||||
|
}
|
Loading…
Reference in New Issue