Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
*:focus-visible {
@apply ring-2 ring-indigo-500 ring-offset-2 dark:ring-offset-gray-900 outline-none;
}

:focus:not(:focus-visible) {
@apply outline-none;
}
}

@layer components {
Expand Down
2 changes: 1 addition & 1 deletion assets/css/modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
position: fixed;
inset: 0;
background: rgb(0 0 0 / 60%);
z-index: 99;
z-index: 999;
overflow: auto;
}

Expand Down
8 changes: 4 additions & 4 deletions assets/js/dashboard/components/notice.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ export function FeatureSetupNotice({
}

return (
<div className="sm:mx-32 mt-6 mb-3">
<div className="py-3">
<div className="text-center text-pretty mt-2 text-gray-800 dark:text-gray-200 font-medium">
<div className="size-full flex items-center justify-center">
<div className="py-3 max-w-2xl">
<div className="text-center text-pretty mt-2 text-gray-800 dark:text-gray-200 font-medium text-pretty">
{title}
</div>

<div className="text-center text-pretty mt-4 font-small text-sm text-gray-500 dark:text-gray-200">
<div className="text-center text-pretty mt-4 font-small text-sm text-gray-500 dark:text-gray-200 text-pretty">
{info}
</div>

Expand Down
20 changes: 20 additions & 0 deletions assets/js/dashboard/components/pill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { ReactNode } from 'react'
import classNames from 'classnames'

export type PillProps = {
className?: string
children: ReactNode
}

export function Pill({ className, children }: PillProps) {
return (
<div
className={classNames(
'flex items-center shrink-0 h-fit rounded-md bg-green-50 dark:bg-green-900/60 text-green-700 dark:text-green-300 text-xs font-medium px-2.5 py-1',
className
)}
>
{children}
</div>
)
}
3 changes: 1 addition & 2 deletions assets/js/dashboard/components/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ const items = {
'data-[selected=true]:bg-gray-100',
'data-[selected=true]:dark:bg-gray-700',
'data-[selected=true]:text-gray-900',
'data-[selected=true]:dark:text-gray-100',
'data-[selected=true]:font-semibold'
'data-[selected=true]:dark:text-gray-100'
),
hoverLink: classNames(
'hover:bg-gray-100',
Expand Down
102 changes: 61 additions & 41 deletions assets/js/dashboard/components/tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Popover, Transition } from '@headlessui/react'
import classNames from 'classnames'
import React, { ReactNode, useRef } from 'react'
import React, { ReactNode, useRef, useEffect } from 'react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import { popover, BlurMenuButtonOnEscape } from './popover'
import { useSearchableItems } from '../hooks/use-searchable-items'
Expand All @@ -16,7 +16,7 @@ export const TabWrapper = ({
}) => (
<div
className={classNames(
'flex text-xs font-medium text-gray-500 dark:text-gray-400 space-x-2 items-baseline',
'flex items-baseline gap-x-3.5 text-xs font-medium text-gray-500 dark:text-gray-400',
className
)}
>
Expand All @@ -32,11 +32,10 @@ const TabButtonText = ({
active: boolean
}) => (
<span
className={classNames('truncate text-left transition-colors duration-150', {
'hover:text-indigo-700 dark:hover:text-indigo-400 cursor-pointer':
className={classNames('truncate text-left text-xs uppercase', {
'text-gray-500 dark:text-gray-400 group-hover/tab:text-gray-800 dark:group-hover/tab:text-gray-200 font-semibold cursor-pointer':
!active,
'text-indigo-600 dark:text-indigo-500 font-bold underline decoration-2 decoration-indigo-600 dark:decoration-indigo-500':
active
'text-gray-900 dark:text-gray-100 font-bold tracking-[-.01em]': active
})}
>
{children}
Expand All @@ -54,9 +53,18 @@ export const TabButton = ({
onClick: () => void
active: boolean
}) => (
<button className={classNames('rounded-sm', className)} onClick={onClick}>
<TabButtonText active={active}>{children}</TabButtonText>
</button>
<div
className={classNames('-mb-px pb-4', {
'border-b-2 border-gray-900 dark:border-gray-100': active
})}
>
<button
className={classNames('group/tab flex rounded-sm', className)}
onClick={onClick}
>
<TabButtonText active={active}>{children}</TabButtonText>
</button>
</div>
)

export const DropdownTabButton = ({
Expand All @@ -78,25 +86,34 @@ export const DropdownTabButton = ({
{({ close: closeDropdown }) => (
<>
<BlurMenuButtonOnEscape targetRef={dropdownButtonRef} />
<Popover.Button
className="inline-flex justify-between rounded-xs"
ref={dropdownButtonRef}
<div
className={classNames('-mb-px pb-4', {
'border-b-2 border-gray-900 dark:border-gray-100': active
})}
>
<TabButtonText active={active}>{children}</TabButtonText>

<div
className="flex self-stretch -mr-1 ml-1 items-center"
aria-hidden="true"
<Popover.Button
className="group/tab inline-flex justify-between rounded-xs"
ref={dropdownButtonRef}
>
<ChevronDownIcon className="h-4 w-4" />
</div>
</Popover.Button>
<TabButtonText active={active}>{children}</TabButtonText>

<div className="ml-0.5 -mr-1" aria-hidden="true">
<ChevronDownIcon
className={classNames('size-4', {
'text-gray-500 dark:text-gray-400 group-hover/tab:text-gray-800 dark:group-hover/tab:text-gray-200':
!active,
'text-gray-900 dark:text-gray-100': active
})}
/>
</div>
</Popover.Button>
</div>

<Transition
as="div"
{...popover.transition.props}
className={classNames(
popover.transition.classNames.fullwidth,
popover.transition.classNames.left,
'mt-2',
transitionClassName
)}
Expand All @@ -115,15 +132,9 @@ type ItemsProps = {
closeDropdown: () => void
options: Array<{ selected: boolean; onClick: () => void; label: string }>
searchable?: boolean
collectionTitle?: string
}

const Items = ({
options,
searchable,
collectionTitle,
closeDropdown
}: ItemsProps) => {
const Items = ({ options, searchable, closeDropdown }: ItemsProps) => {
const {
filteredData,
showableData,
Expand All @@ -136,7 +147,7 @@ const Items = ({
countOfMoreToShow
} = useSearchableItems({
data: options,
maxItemsInitially: searchable ? 5 : options.length,
maxItemsInitially: searchable ? 10 : options.length,
itemMatchesSearchValue: (option, trimmedSearchString) =>
option.label.toLowerCase().includes(trimmedSearchString.toLowerCase())
})
Expand All @@ -148,24 +159,30 @@ const Items = ({
popover.items.classNames.hoverLink
)

useEffect(() => {
if (searchable && showSearch && searchRef.current) {
const timeoutId = setTimeout(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Why are we focusing the search in 100ms and not instantly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delay is to account for the animation of the popover.

searchRef.current?.focus()
}, 100)
return () => clearTimeout(timeoutId)
}
}, [searchable, showSearch, searchRef])

return (
<>
{searchable && showSearch && (
<div className="flex items-center py-2 px-4">
{collectionTitle && (
<div className="text-sm font-bold uppercase text-indigo-500 dark:text-indigo-400 mr-4">
{collectionTitle}
</div>
)}
<div className="flex items-center p-1">
<SearchInput
searchRef={searchRef}
placeholderUnfocused="Press / to search"
className="ml-auto w-full py-1"
className="w-full !max-w-none"
onSearch={handleSearchInput}
/>
</div>
)}
<div className={'max-h-[210px] overflow-y-scroll'}>
<div
className={'max-h-[224px] overflow-y-auto flex flex-col gap-y-0.5 p-1'}
>
{showableData.map(({ selected, label, onClick }, index) => {
return (
<button
Expand All @@ -177,7 +194,7 @@ const Items = ({
data-selected={selected}
className={itemClassName}
>
{label}
<span className="line-clamp-1">{label}</span>
</button>
)
})}
Expand All @@ -186,7 +203,7 @@ const Items = ({
onClick={handleShowAll}
className={classNames(
itemClassName,
'w-full text-left font-bold hover:text-indigo-700 dark:hover:text-indigo-500'
'w-full text-left text-gray-500 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200'
)}
>
{`Show ${countOfMoreToShow} more`}
Expand All @@ -197,11 +214,14 @@ const Items = ({
<button
className={classNames(
itemClassName,
'w-full text-left font-bold hover:text-indigo-700 dark:hover:text-indigo-500'
'w-full text-left !justify-start'
)}
onClick={handleClearSearch}
>
No items found. Clear search to show all.
No items found.{' '}
<span className="ml-1 text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-500">
Click to clear search.
</span>
</button>
)}
</div>
Expand Down
6 changes: 4 additions & 2 deletions assets/js/dashboard/extra/funnel.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,9 @@ export default function Funnel({ funnelName, tabs }) {
const header = () => {
return (
<div className="flex justify-between w-full">
<h4 className="mt-2 text-sm dark:text-gray-100">{funnelName}</h4>
<h4 className="mt-2 text-base font-semibold dark:text-gray-100">
{funnelName}
</h4>
{tabs}
</div>
)
Expand Down Expand Up @@ -346,7 +348,7 @@ export default function Funnel({ funnelName, tabs }) {
return (
<div className="mb-8">
{header()}
<p className="mt-1 text-gray-500 text-sm">
<p className="mt-0.5 text-gray-500 text-sm">
{funnel.steps.length}-step funnel • {conversionRate}% conversion
rate
</p>
Expand Down
40 changes: 12 additions & 28 deletions assets/js/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,37 +50,21 @@ function DashboardStats({
}
}, [onLiveNavigate])

const statsBoxClass =
'relative min-h-[436px] w-full mt-5 p-4 flex flex-col bg-white dark:bg-gray-900 shadow-sm rounded-md md:min-h-initial md:h-27.25rem md:w-[calc(50%-10px)] md:ml-[10px] md:mr-[10px] first:ml-0 last:mr-0'

return (
<>
<VisitorGraph updateImportedDataInView={updateImportedDataInView} />
<div className="w-full md:flex">
<div className={statsBoxClass}>
<Sources />
</div>
<div className={statsBoxClass}>
{site.flags.live_dashboard ? (
<LiveViewPortal
id="pages-breakdown-live"
className="w-full h-full border-0 overflow-hidden"
/>
) : (
<Pages />
)}
</div>
</div>

<div className="w-full md:flex">
<div className={statsBoxClass}>
<Locations />
</div>
<div className={statsBoxClass}>
<Devices />
</div>
</div>
<Sources />
{site.flags.live_dashboard ? (
<LiveViewPortal
id="pages-breakdown-live"
className="w-full h-full border-0 overflow-hidden"
/>
) : (
<Pages />
)}

<Locations />
<Devices />
<Behaviours importedDataInView={importedDataInView} />
</>
)
Expand All @@ -98,7 +82,7 @@ function Dashboard() {
const [importedDataInView, setImportedDataInView] = useState(false)

return (
<div className="mb-16">
<div className="mb-16 grid grid-cols-1 md:grid-cols-2 gap-5">
<TopBar showCurrentVisitors={!isRealTimeDashboard} />
<DashboardStats
importedDataInView={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const SearchableSegmentsSection = ({
)}
</div>

<div className="max-h-[210px] overflow-y-scroll">
<div className="max-h-[228px] overflow-y-auto">
{showableData.map((segment) => {
return (
<Tooltip
Expand Down
6 changes: 3 additions & 3 deletions assets/js/dashboard/nav-menu/top-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ function TopBarStickyWrapper({ children }: { children: ReactNode }) {

return (
<>
<div id="stats-container-top" ref={ref} />
<div id="stats-container-top" className="col-span-full" ref={ref} />
<div
className={classNames(
'relative top-0 py-2 sm:py-3 z-10',
'col-span-full relative top-0 py-2 sm:py-3 -my-3 sm:-my-4 z-10',
!site.embedded &&
!inView &&
'sticky fullwidth-shadow bg-gray-50 dark:bg-gray-950'
Expand All @@ -47,7 +47,7 @@ function TopBarInner({ showCurrentVisitors }: TopBarProps) {

return (
<div className="flex items-center w-full">
<div className="flex items-center gap-x-4 shrink-0" ref={leftActionsRef}>
<div className="flex items-center gap-x-5 shrink-0" ref={leftActionsRef}>
<SiteSwitcher />
{showCurrentVisitors && (
<CurrentVisitors tooltipBoundaryRef={leftActionsRef} />
Expand Down
4 changes: 2 additions & 2 deletions assets/js/dashboard/site-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export const SiteSwitcher = () => {
<Popover.Button
ref={buttonRef}
className={classNames(
'flex items-center rounded h-9 leading-5 font-bold dark:text-gray-100',
'flex items-center rounded h-9 text-sm leading-5 font-semibold dark:text-gray-100',
'hover:bg-gray-100 dark:hover:bg-gray-800'
)}
title={currentSite.domain}
Expand All @@ -188,7 +188,7 @@ export const SiteSwitcher = () => {
? 'All sites'
: currentSite.domain}
</span>
<ChevronDownIcon className="hidden lg:block h-5 w-5 ml-2 dark:text-gray-100" />
<ChevronDownIcon className="hidden lg:block size-4 ml-2 dark:text-gray-100" />
</Popover.Button>
<Transition
as="div"
Expand Down
Loading
Loading