mirror of
https://github.com/enso-org/enso.git
synced 2024-11-30 15:45:11 +03:00
Separated templates and directory list (#6995)
Closes https://github.com/enso-org/cloud-v2/issues/490: - Make templates collapsible - Add scroll-based shadow to templates - Move scrollbars from body to templates and directory listing # Important Notes None
This commit is contained in:
parent
66894bd79e
commit
8504295e5e
4
app/ide-desktop/lib/assets/rotating_arrow.svg
Normal file
4
app/ide-desktop/lib/assets/rotating_arrow.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 4.93L5 11.07A.5.5 0 0 0 5.77 11.5L10.4 8.4A.5.5 0 0 0 10.4 7.6L5.77 4.5A.5.5 0 0 0 5 4.93"
|
||||||
|
fill="#3e515fe5" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 225 B |
@ -1061,7 +1061,7 @@ function Dashboard(props: DashboardProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex flex-col relative select-none text-primary text-xs min-h-screen p-2 ${
|
className={`flex flex-col gap-2 relative select-none text-primary text-xs h-screen py-2 ${
|
||||||
tab === Tab.dashboard ? '' : 'hidden'
|
tab === Tab.dashboard ? '' : 'hidden'
|
||||||
}`}
|
}`}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
@ -1091,21 +1091,21 @@ function Dashboard(props: DashboardProps) {
|
|||||||
setQuery={setQuery}
|
setQuery={setQuery}
|
||||||
/>
|
/>
|
||||||
{isListingRemoteDirectoryWhileOffline ? (
|
{isListingRemoteDirectoryWhileOffline ? (
|
||||||
<div className="grow grid place-items-center">
|
<div className="grow grid place-items-center mx-2">
|
||||||
<div className="text-base text-center">
|
<div className="text-base text-center">
|
||||||
You are offline. Please connect to the internet and refresh to access the
|
You are offline. Please connect to the internet and refresh to access the
|
||||||
cloud backend.
|
cloud backend.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : isListingLocalDirectoryAndWillFail ? (
|
) : isListingLocalDirectoryAndWillFail ? (
|
||||||
<div className="grow grid place-items-center">
|
<div className="grow grid place-items-center mx-2">
|
||||||
<div className="text-base text-center">
|
<div className="text-base text-center">
|
||||||
Could not connect to the Project Manager. Please try restarting{' '}
|
Could not connect to the Project Manager. Please try restarting{' '}
|
||||||
{common.PRODUCT_NAME}, or manually launching the Project Manager.
|
{common.PRODUCT_NAME}, or manually launching the Project Manager.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : isListingRemoteDirectoryAndWillFail ? (
|
) : isListingRemoteDirectoryAndWillFail ? (
|
||||||
<div className="grow grid place-items-center">
|
<div className="grow grid place-items-center mx-2">
|
||||||
<div className="text-base text-center">
|
<div className="text-base text-center">
|
||||||
We will review your user details and enable the cloud experience for you
|
We will review your user details and enable the cloud experience for you
|
||||||
shortly.
|
shortly.
|
||||||
@ -1114,7 +1114,7 @@ function Dashboard(props: DashboardProps) {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Templates onTemplateClick={handleCreateProject} />
|
<Templates onTemplateClick={handleCreateProject} />
|
||||||
<div className="flex flex-row flex-nowrap my-2">
|
<div className="flex flex-row flex-nowrap mx-2">
|
||||||
<h1 className="text-xl font-bold mx-4 self-center">Drive</h1>
|
<h1 className="text-xl font-bold mx-4 self-center">Drive</h1>
|
||||||
<div className="flex flex-row flex-nowrap mx-4">
|
<div className="flex flex-row flex-nowrap mx-4">
|
||||||
{backend.type === backendModule.BackendType.remote && (
|
{backend.type === backendModule.BackendType.remote && (
|
||||||
@ -1224,9 +1224,12 @@ function Dashboard(props: DashboardProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table className="table-fixed items-center border-collapse mt-2 w-0">
|
{/* Padding. */}
|
||||||
|
<div className="h-6 mx-2" />
|
||||||
|
<div className="flex-1 overflow-auto mx-2">
|
||||||
|
<table className="table-fixed items-center border-collapse mt-2">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr className="h-10">
|
<tr className="h-0">
|
||||||
{columnsFor(columnDisplayMode, backend.type).map(column => (
|
{columnsFor(columnDisplayMode, backend.type).map(column => (
|
||||||
<td key={column} className={COLUMN_CSS_CLASS[column]} />
|
<td key={column} className={COLUMN_CSS_CLASS[column]} />
|
||||||
))}
|
))}
|
||||||
@ -1237,8 +1240,8 @@ function Dashboard(props: DashboardProps) {
|
|||||||
isLoading={isLoadingAssets}
|
isLoading={isLoadingAssets}
|
||||||
placeholder={
|
placeholder={
|
||||||
<span className="opacity-75">
|
<span className="opacity-75">
|
||||||
You have no project yet. Go ahead and create one using the
|
You have no projects yet. Go ahead and create one using
|
||||||
form above.
|
the form above.
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
columns={columnsFor(columnDisplayMode, backend.type).map(
|
columns={columnsFor(columnDisplayMode, backend.type).map(
|
||||||
@ -1248,7 +1251,10 @@ function Dashboard(props: DashboardProps) {
|
|||||||
column,
|
column,
|
||||||
backendModule.AssetType.project
|
backendModule.AssetType.project
|
||||||
),
|
),
|
||||||
render: renderer(column, backendModule.AssetType.project),
|
render: renderer(
|
||||||
|
column,
|
||||||
|
backendModule.AssetType.project
|
||||||
|
),
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
onClick={(projectAsset, event) => {
|
onClick={(projectAsset, event) => {
|
||||||
@ -1271,8 +1277,8 @@ function Dashboard(props: DashboardProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
const doOpenAsFolder = () => {
|
const doOpenAsFolder = () => {
|
||||||
// FIXME[sb]: Uncomment once backend support
|
// TODO[sb]: Implement once backend support is in place.
|
||||||
// is in place.
|
// https://github.com/enso-org/cloud-v2/issues/506
|
||||||
// The following code does not typecheck
|
// The following code does not typecheck
|
||||||
// since `ProjectId`s are not `DirectoryId`s.
|
// since `ProjectId`s are not `DirectoryId`s.
|
||||||
// enterDirectory(projectAsset)
|
// enterDirectory(projectAsset)
|
||||||
@ -1302,11 +1308,14 @@ function Dashboard(props: DashboardProps) {
|
|||||||
newName
|
newName
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await backend.projectUpdate(projectAsset.id, {
|
await backend.projectUpdate(
|
||||||
|
projectAsset.id,
|
||||||
|
{
|
||||||
ami: null,
|
ami: null,
|
||||||
ideVersion: null,
|
ideVersion: null,
|
||||||
projectName: newName,
|
projectName: newName,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
onComplete={doRefresh}
|
onComplete={doRefresh}
|
||||||
/>
|
/>
|
||||||
@ -1326,13 +1335,15 @@ function Dashboard(props: DashboardProps) {
|
|||||||
projectAsset
|
projectAsset
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return backend.deleteProject(projectAsset.id)
|
return backend.deleteProject(
|
||||||
|
projectAsset.id
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
onComplete={doRefresh}
|
onComplete={doRefresh}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
const isDisabled =
|
const isDeleteDisabled =
|
||||||
backend.type === backendModule.BackendType.local &&
|
backend.type === backendModule.BackendType.local &&
|
||||||
(projectDatas[projectAsset.id]?.isRunning ?? false)
|
(projectDatas[projectAsset.id]?.isRunning ?? false)
|
||||||
setModal(() => (
|
setModal(() => (
|
||||||
@ -1340,8 +1351,12 @@ function Dashboard(props: DashboardProps) {
|
|||||||
<ContextMenuEntry onClick={doOpenForEditing}>
|
<ContextMenuEntry onClick={doOpenForEditing}>
|
||||||
Open for editing
|
Open for editing
|
||||||
</ContextMenuEntry>
|
</ContextMenuEntry>
|
||||||
{backend.type !== backendModule.BackendType.local && (
|
{backend.type !==
|
||||||
<ContextMenuEntry disabled onClick={doOpenAsFolder}>
|
backendModule.BackendType.local && (
|
||||||
|
<ContextMenuEntry
|
||||||
|
disabled
|
||||||
|
onClick={doOpenAsFolder}
|
||||||
|
>
|
||||||
Open as folder
|
Open as folder
|
||||||
</ContextMenuEntry>
|
</ContextMenuEntry>
|
||||||
)}
|
)}
|
||||||
@ -1349,8 +1364,8 @@ function Dashboard(props: DashboardProps) {
|
|||||||
Rename
|
Rename
|
||||||
</ContextMenuEntry>
|
</ContextMenuEntry>
|
||||||
<ContextMenuEntry
|
<ContextMenuEntry
|
||||||
disabled={isDisabled}
|
disabled={isDeleteDisabled}
|
||||||
{...(isDisabled
|
{...(isDeleteDisabled
|
||||||
? {
|
? {
|
||||||
title: 'A running local project cannot be removed.',
|
title: 'A running local project cannot be removed.',
|
||||||
}
|
}
|
||||||
@ -1415,7 +1430,9 @@ function Dashboard(props: DashboardProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<tr className="h-10" />
|
<tr className="h-10" />
|
||||||
<Rows<backendModule.Asset<backendModule.AssetType.secret>>
|
<Rows<
|
||||||
|
backendModule.Asset<backendModule.AssetType.secret>
|
||||||
|
>
|
||||||
items={visibleSecretAssets}
|
items={visibleSecretAssets}
|
||||||
getKey={secret => secret.id}
|
getKey={secret => secret.id}
|
||||||
isLoading={isLoadingAssets}
|
isLoading={isLoadingAssets}
|
||||||
@ -1554,10 +1571,16 @@ function Dashboard(props: DashboardProps) {
|
|||||||
}
|
}
|
||||||
setModal(() => (
|
setModal(() => (
|
||||||
<ContextMenu key={file.id} event={event}>
|
<ContextMenu key={file.id} event={event}>
|
||||||
<ContextMenuEntry disabled onClick={doCopy}>
|
<ContextMenuEntry
|
||||||
|
disabled
|
||||||
|
onClick={doCopy}
|
||||||
|
>
|
||||||
Copy
|
Copy
|
||||||
</ContextMenuEntry>
|
</ContextMenuEntry>
|
||||||
<ContextMenuEntry disabled onClick={doCut}>
|
<ContextMenuEntry
|
||||||
|
disabled
|
||||||
|
onClick={doCut}
|
||||||
|
>
|
||||||
Cut
|
Cut
|
||||||
</ContextMenuEntry>
|
</ContextMenuEntry>
|
||||||
<ContextMenuEntry onClick={doDelete}>
|
<ContextMenuEntry onClick={doDelete}>
|
||||||
@ -1579,6 +1602,7 @@ function Dashboard(props: DashboardProps) {
|
|||||||
))(backend)}
|
))(backend)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
{isFileBeingDragged &&
|
{isFileBeingDragged &&
|
||||||
directoryId != null &&
|
directoryId != null &&
|
||||||
backend.type === backendModule.BackendType.remote ? (
|
backend.type === backendModule.BackendType.remote ? (
|
||||||
|
@ -1,5 +1,35 @@
|
|||||||
/** @file Renders the list of templates from which a project can be created. */
|
/** @file Renders the list of templates from which a project can be created. */
|
||||||
|
import * as react from 'react'
|
||||||
|
|
||||||
import PlusCircledIcon from 'enso-assets/plus_circled.svg'
|
import PlusCircledIcon from 'enso-assets/plus_circled.svg'
|
||||||
|
import RotatingArrowIcon from 'enso-assets/rotating_arrow.svg'
|
||||||
|
|
||||||
|
import * as common from 'enso-common'
|
||||||
|
|
||||||
|
// =================
|
||||||
|
// === Constants ===
|
||||||
|
// =================
|
||||||
|
|
||||||
|
/** The `localStorage` key used to store whether the {@link Templates} element should be
|
||||||
|
* expanded by default. */
|
||||||
|
const IS_TEMPLATES_OPEN_KEY = `${common.PRODUCT_NAME.toLowerCase()}-is-templates-expanded`
|
||||||
|
/** The max width at which the bottom shadow should be visible. */
|
||||||
|
const MAX_WIDTH_NEEDING_SCROLL = 1031
|
||||||
|
/** The height of the bottom padding - 8px for the grid gap, and another 8px for the height
|
||||||
|
* of the padding div. */
|
||||||
|
const PADDING_HEIGHT = 16
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// === Types ===
|
||||||
|
// =============
|
||||||
|
|
||||||
|
/** The CSS class to apply inset shadows on the specified side(s). */
|
||||||
|
enum ShadowClass {
|
||||||
|
none = '',
|
||||||
|
top = 'shadow-inset-t-lg',
|
||||||
|
bottom = 'shadow-inset-b-lg',
|
||||||
|
both = 'shadow-inset-v-lg',
|
||||||
|
}
|
||||||
|
|
||||||
// =================
|
// =================
|
||||||
// === Templates ===
|
// === Templates ===
|
||||||
@ -77,10 +107,8 @@ function TemplatesRender(props: TemplatesRenderProps) {
|
|||||||
className="h-40 cursor-pointer"
|
className="h-40 cursor-pointer"
|
||||||
>
|
>
|
||||||
<div className="flex h-full w-full border-dashed-custom rounded-2xl text-primary">
|
<div className="flex h-full w-full border-dashed-custom rounded-2xl text-primary">
|
||||||
<div className="m-auto text-center">
|
<div className="flex flex-col text-center items-center m-auto">
|
||||||
<button>
|
|
||||||
<img src={PlusCircledIcon} />
|
<img src={PlusCircledIcon} />
|
||||||
</button>
|
|
||||||
<p className="font-semibold text-sm">New empty project</p>
|
<p className="font-semibold text-sm">New empty project</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -130,10 +158,94 @@ export interface TemplatesProps {
|
|||||||
function Templates(props: TemplatesProps) {
|
function Templates(props: TemplatesProps) {
|
||||||
const { onTemplateClick } = props
|
const { onTemplateClick } = props
|
||||||
|
|
||||||
|
const [shadowClass, setShadowClass] = react.useState(
|
||||||
|
window.innerWidth <= MAX_WIDTH_NEEDING_SCROLL ? ShadowClass.bottom : ShadowClass.none
|
||||||
|
)
|
||||||
|
const [isOpen, setIsOpen] = react.useState(() => {
|
||||||
|
/** This must not be in a `useEffect` as it would flash open for one frame.
|
||||||
|
* It can be in a `useLayoutEffect` but as that needs to be checked every re-render,
|
||||||
|
* this is slightly more performant. */
|
||||||
|
const savedIsOpen = localStorage.getItem(IS_TEMPLATES_OPEN_KEY)
|
||||||
|
let result = true
|
||||||
|
if (savedIsOpen != null) {
|
||||||
|
try {
|
||||||
|
result = JSON.parse(savedIsOpen) !== false
|
||||||
|
} catch {
|
||||||
|
// Ignored. This should only happen when a user manually sets invalid JSON into
|
||||||
|
// the `localStorage` key used by this component.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
// This is incorrect, but SAFE, as its value will always be assigned before any hooks are run.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const containerRef = react.useRef<HTMLDivElement>(null!)
|
||||||
|
|
||||||
|
const toggleIsOpen = react.useCallback(() => {
|
||||||
|
setIsOpen(oldIsOpen => !oldIsOpen)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const updateShadowClass = () => {
|
||||||
|
const element = containerRef.current
|
||||||
|
const boundingBox = element.getBoundingClientRect()
|
||||||
|
let newShadowClass: ShadowClass
|
||||||
|
const shouldShowTopShadow = element.scrollTop !== 0
|
||||||
|
// `window.innerWidth <= MAX_WIDTH_NEEDING_SCROLL` is repeated. This is intentional,
|
||||||
|
// to avoid adding it as a dependency.
|
||||||
|
const paddingHeight = window.innerWidth <= MAX_WIDTH_NEEDING_SCROLL ? 0 : PADDING_HEIGHT
|
||||||
|
// Chrome has decimal places in its bounding box, which can overshoot the target size
|
||||||
|
// slightly.
|
||||||
|
const shouldShowBottomShadow =
|
||||||
|
element.scrollTop + boundingBox.height + paddingHeight + 1 < element.scrollHeight
|
||||||
|
if (shouldShowTopShadow && shouldShowBottomShadow) {
|
||||||
|
newShadowClass = ShadowClass.both
|
||||||
|
} else if (shouldShowTopShadow) {
|
||||||
|
newShadowClass = ShadowClass.top
|
||||||
|
} else if (shouldShowBottomShadow) {
|
||||||
|
newShadowClass = ShadowClass.bottom
|
||||||
|
} else {
|
||||||
|
newShadowClass = ShadowClass.none
|
||||||
|
}
|
||||||
|
setShadowClass(newShadowClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
react.useEffect(() => {
|
||||||
|
window.addEventListener('resize', updateShadowClass)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', updateShadowClass)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
react.useEffect(() => {
|
||||||
|
localStorage.setItem(IS_TEMPLATES_OPEN_KEY, JSON.stringify(isOpen))
|
||||||
|
}, [isOpen])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-2 p-2">
|
<div className="mx-2">
|
||||||
<div className="grid gap-2 grid-cols-fill-60-minmax-scrollbar-aware justify-center">
|
<div className="flex items-center my-2">
|
||||||
|
<div className="w-4">
|
||||||
|
<div
|
||||||
|
className={`cursor-pointer transition-all ease-in-out ${
|
||||||
|
isOpen ? 'rotate-90' : ''
|
||||||
|
}`}
|
||||||
|
onClick={toggleIsOpen}
|
||||||
|
>
|
||||||
|
<img src={RotatingArrowIcon} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-xl font-bold self-center">Templates</h1>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={`grid gap-2 grid-cols-fill-60 justify-center overflow-y-scroll scroll-hidden transition-all duration-300 ease-in-out px-4 ${
|
||||||
|
isOpen ? `h-templates-custom ${shadowClass}` : 'h-0'
|
||||||
|
}`}
|
||||||
|
onScroll={updateShadowClass}
|
||||||
|
>
|
||||||
<TemplatesRender templates={TEMPLATES} onTemplateClick={onTemplateClick} />
|
<TemplatesRender templates={TEMPLATES} onTemplateClick={onTemplateClick} />
|
||||||
|
{/* Spacing. */}
|
||||||
|
<div className="col-span-full h-2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -57,7 +57,7 @@ function TopBar(props: TopBarProps) {
|
|||||||
}, [isUserMenuVisible])
|
}, [isUserMenuVisible])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex mb-2 h-8">
|
<div className="flex mx-2 h-8">
|
||||||
{supportsLocalBackend && (
|
{supportsLocalBackend && (
|
||||||
<div className="bg-gray-100 rounded-full flex flex-row flex-nowrap p-1.5">
|
<div className="bg-gray-100 rounded-full flex flex-row flex-nowrap p-1.5">
|
||||||
<button
|
<button
|
||||||
|
@ -107,73 +107,19 @@ body {
|
|||||||
background-image: url("enso-assets/dashed_border.svg");
|
background-image: url("enso-assets/dashed_border.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-cols-fill-60-minmax-scrollbar-aware {
|
.scroll-hidden {
|
||||||
/* Graceful degradation for extremely large monitors. */
|
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||||
@media screen and (min-width: 1275px) {
|
scrollbar-width: none; /* Firefox */
|
||||||
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
|
}
|
||||||
|
.scroll-hidden::-webkit-scrollbar {
|
||||||
|
display: none; /* Safari and Chrome */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Each column is 240px (tile) + 8px (padding). */
|
.h-templates-custom {
|
||||||
/* Hardcore scrollbar-aware values for monitors up to 3840px wide. */
|
height: 21.5rem;
|
||||||
|
|
||||||
@media screen and (min-width: 3755px) and (max-width: 4002px) {
|
@media screen and (min-width: 1771px) {
|
||||||
grid-template-columns: repeat(14, minmax(15rem, 1fr));
|
height: 11rem;
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 3507px) and (max-width: 3754px) {
|
|
||||||
grid-template-columns: repeat(14, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 3259px) and (max-width: 3506px) {
|
|
||||||
grid-template-columns: repeat(13, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 3011px) and (max-width: 3258px) {
|
|
||||||
grid-template-columns: repeat(12, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 2763px) and (max-width: 3010px) {
|
|
||||||
grid-template-columns: repeat(11, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 2515px) and (max-width: 2762px) {
|
|
||||||
grid-template-columns: repeat(10, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 2267px) and (max-width: 2514px) {
|
|
||||||
grid-template-columns: repeat(9, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 2019px) and (max-width: 2266px) {
|
|
||||||
grid-template-columns: repeat(8, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1771px) and (max-width: 2018px) {
|
|
||||||
grid-template-columns: repeat(7, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1523px) and (max-width: 1770px) {
|
|
||||||
grid-template-columns: repeat(6, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1275px) and (max-width: 1522px) {
|
|
||||||
grid-template-columns: repeat(5, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1027px) and (max-width: 1274px) {
|
|
||||||
grid-template-columns: repeat(4, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 779px) and (max-width: 1026px) {
|
|
||||||
grid-template-columns: repeat(3, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 531px) and (max-width: 778px) {
|
|
||||||
grid-template-columns: repeat(2, minmax(15rem, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 530) {
|
|
||||||
grid-template-columns: repeat(1, 1fr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,21 @@ export const theme = {
|
|||||||
soft: `0 0.5px 2.2px 0px #00000008, 0 1.2px 5.3px 0px #0000000b, \
|
soft: `0 0.5px 2.2px 0px #00000008, 0 1.2px 5.3px 0px #0000000b, \
|
||||||
0 2.3px 10px 0 #0000000e, 0 4px 18px 0 #00000011, 0 7.5px 33.4px 0 #00000014, \
|
0 2.3px 10px 0 #0000000e, 0 4px 18px 0 #00000011, 0 7.5px 33.4px 0 #00000014, \
|
||||||
0 18px 80px 0 #0000001c`,
|
0 18px 80px 0 #0000001c`,
|
||||||
|
'inset-t-lg': `inset 0 1px 1.4px -1.4px #00000002, \
|
||||||
|
inset 0 2.4px 3.4px -3.4px #00000003, inset 0 4.5px 6.4px -6.4px #00000004, \
|
||||||
|
inset 0 8px 11.4px -11.4px #00000005, inset 0 15px 21.3px -21.3px #00000006, \
|
||||||
|
inset 0 36px 51px -51px #00000014`,
|
||||||
|
'inset-b-lg': `inset 0 -1px 1.4px -1.4px #00000002, \
|
||||||
|
inset 0 -2.4px 3.4px -3.4px #00000003, inset 0 -4.5px 6.4px -6.4px #00000004, \
|
||||||
|
inset 0 -8px 11.4px -11.4px #00000005, inset 0 -15px 21.3px -21.3px #00000006, \
|
||||||
|
inset 0 -36px 51px -51px #00000014`,
|
||||||
|
'inset-v-lg': `inset 0 1px 1.4px -1.4px #00000002, \
|
||||||
|
inset 0 2.4px 3.4px -3.4px #00000003, inset 0 4.5px 6.4px -6.4px #00000004, \
|
||||||
|
inset 0 8px 11.4px -11.4px #00000005, inset 0 15px 21.3px -21.3px #00000006, \
|
||||||
|
inset 0 36px 51px -51px #00000014, inset 0 -1px 1.4px -1.4px #00000002, \
|
||||||
|
inset 0 -2.4px 3.4px -3.4px #00000003, inset 0 -4.5px 6.4px -6.4px #00000004, \
|
||||||
|
inset 0 -8px 11.4px -11.4px #00000005, inset 0 -15px 21.3px -21.3px #00000006, \
|
||||||
|
inset 0 -36px 51px -51px #00000014`,
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
'spin-ease': 'spin cubic-bezier(0.67, 0.33, 0.33, 0.67) 1.5s infinite',
|
'spin-ease': 'spin cubic-bezier(0.67, 0.33, 0.33, 0.67) 1.5s infinite',
|
||||||
@ -59,5 +74,8 @@ export const theme = {
|
|||||||
transitionDuration: {
|
transitionDuration: {
|
||||||
'90000': '90000ms',
|
'90000': '90000ms',
|
||||||
},
|
},
|
||||||
|
gridTemplateColumns: {
|
||||||
|
'fill-60': 'repeat(auto-fill, minmax(15rem, 1fr))',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user