mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 20:57:17 +01:00
Responsive everything.
This commit is contained in:
parent
9df012084d
commit
4d2a7ea947
23 changed files with 286 additions and 287 deletions
|
|
@ -1,5 +1,13 @@
|
|||
import { useState, Key, Fragment } from 'react';
|
||||
import { Modal, Select, ListItem, ListSeparator, Dialog, SelectProps } from '@umami/react-zen';
|
||||
import {
|
||||
Modal,
|
||||
Select,
|
||||
ListItem,
|
||||
ListSeparator,
|
||||
Dialog,
|
||||
SelectProps,
|
||||
useBreakpoint,
|
||||
} from '@umami/react-zen';
|
||||
import { endOfYear } from 'date-fns';
|
||||
import { DatePickerForm } from '@/components/metrics/DatePickerForm';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
|
|
@ -11,7 +19,7 @@ export interface DateFilterProps extends SelectProps {
|
|||
onChange?: (value: string) => void;
|
||||
showAllTime?: boolean;
|
||||
renderDate?: boolean;
|
||||
placement?: string;
|
||||
placement?: any;
|
||||
}
|
||||
|
||||
export function DateFilter({
|
||||
|
|
@ -25,6 +33,8 @@ export function DateFilter({
|
|||
const { formatMessage, labels } = useMessages();
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
const { startDate, endDate } = parseDateRange(value) || {};
|
||||
const breakpoint = useBreakpoint();
|
||||
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
|
||||
|
||||
const options = [
|
||||
{ label: formatMessage(labels.today), value: '0day' },
|
||||
|
|
@ -109,7 +119,7 @@ export function DateFilter({
|
|||
placeholder={formatMessage(labels.selectDate)}
|
||||
onChange={handleChange}
|
||||
renderValue={renderValue}
|
||||
popoverProps={{ placement: placement as any }}
|
||||
popoverProps={{ placement, isNonModal: isMobile }}
|
||||
>
|
||||
{options.map(({ label, value, divider }: any) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,20 @@
|
|||
import { Key } from 'react';
|
||||
import { subMonths, endOfDay } from 'date-fns';
|
||||
import { Grid, Column, List, ListItem } from '@umami/react-zen';
|
||||
import {
|
||||
Grid,
|
||||
Column,
|
||||
List,
|
||||
ListItem,
|
||||
Row,
|
||||
Button,
|
||||
Popover,
|
||||
MenuTrigger,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Icon,
|
||||
} from '@umami/react-zen';
|
||||
import { useFields, useMessages } from '@/components/hooks';
|
||||
import { Plus } from '@/components/icons';
|
||||
import { FilterRecord } from '@/components/common/FilterRecord';
|
||||
import { Empty } from '@/components/common/Empty';
|
||||
|
||||
|
|
@ -39,8 +52,31 @@ export function FieldFilters({ websiteId, value, exclude = [], onChange }: Field
|
|||
};
|
||||
|
||||
return (
|
||||
<Grid columns="160px 1fr" overflow="hidden" gapY="6">
|
||||
<Column border="right" paddingRight="3">
|
||||
<Grid columns={{ xs: '1fr', md: '180px 1fr' }} overflow="hidden" gapY="6">
|
||||
<Row display={{ xs: 'flex', md: 'none' }}>
|
||||
<MenuTrigger>
|
||||
<Button>
|
||||
<Icon>
|
||||
<Plus />
|
||||
</Icon>
|
||||
</Button>
|
||||
<Popover placement="bottom start">
|
||||
<Menu onAction={handleAdd}>
|
||||
{fields
|
||||
.filter(({ name }) => !exclude.includes(name))
|
||||
.map(field => {
|
||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||
return (
|
||||
<MenuItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||
{field.label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Popover>
|
||||
</MenuTrigger>
|
||||
</Row>
|
||||
<Column display={{ xs: 'none', md: 'flex' }} border="right" paddingRight="3" marginRight="6">
|
||||
<List onAction={handleAdd}>
|
||||
{fields
|
||||
.filter(({ name }) => !exclude.includes(name))
|
||||
|
|
@ -54,7 +90,7 @@ export function FieldFilters({ websiteId, value, exclude = [], onChange }: Field
|
|||
})}
|
||||
</List>
|
||||
</Column>
|
||||
<Column paddingLeft="6" overflow="auto" gapY="4" height="500px" style={{ contain: 'layout' }}>
|
||||
<Column overflow="auto" gapY="4" style={{ contain: 'layout' }}>
|
||||
{value.map(filter => {
|
||||
return (
|
||||
<FilterRecord
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useFilters, useMessages, useNavigation } from '@/components/hooks';
|
||||
import { FieldFilters } from '@/components/input/FieldFilters';
|
||||
import { SegmentFilters } from '@/components/input/SegmentFilters';
|
||||
import { Button, Column, Row, Tab, TabList, TabPanel, Tabs } from '@umami/react-zen';
|
||||
import { Button, Column, Row, Tab, TabList, TabPanel, Tabs, useBreakpoint } from '@umami/react-zen';
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface FilterEditFormProps {
|
||||
|
|
@ -20,9 +20,8 @@ export function FilterEditForm({ websiteId, onChange, onClose }: FilterEditFormP
|
|||
const [currentFilters, setCurrentFilters] = useState(filters);
|
||||
const [currentSegment, setCurrentSegment] = useState(segment);
|
||||
const [currentCohort, setCurrentCohort] = useState(cohort);
|
||||
const panelProps = {
|
||||
style: { height: 500 },
|
||||
};
|
||||
const breakpoint = useBreakpoint();
|
||||
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
|
||||
const excludeFilters = pathname.includes('/pixels') || pathname.includes('/links');
|
||||
|
||||
const handleReset = () => {
|
||||
|
|
@ -46,42 +45,44 @@ export function FilterEditForm({ websiteId, onChange, onClose }: FilterEditFormP
|
|||
};
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab id="fields">{formatMessage(labels.fields)}</Tab>
|
||||
{!excludeFilters && (
|
||||
<>
|
||||
<Tab id="segments">{formatMessage(labels.segments)}</Tab>
|
||||
<Tab id="cohorts">{formatMessage(labels.cohorts)}</Tab>
|
||||
</>
|
||||
)}
|
||||
</TabList>
|
||||
<TabPanel id="fields" {...panelProps}>
|
||||
<FieldFilters
|
||||
websiteId={websiteId}
|
||||
value={currentFilters}
|
||||
onChange={setCurrentFilters}
|
||||
exclude={excludeFilters ? ['path', 'title', 'hostname', 'tag', 'event'] : []}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel id="segments" {...panelProps}>
|
||||
<SegmentFilters
|
||||
websiteId={websiteId}
|
||||
segmentId={currentSegment}
|
||||
onChange={handleSegmentChange}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel id="cohorts" {...panelProps}>
|
||||
<SegmentFilters
|
||||
type="cohort"
|
||||
websiteId={websiteId}
|
||||
segmentId={currentCohort}
|
||||
onChange={handleSegmentChange}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
<Row alignItems="center" justifyContent="space-between" gridColumn="span 2" marginTop="6" gap>
|
||||
<Column width={isMobile ? 'auto' : '800px'} gap="6">
|
||||
<Column minHeight="500px">
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab id="fields">{formatMessage(labels.fields)}</Tab>
|
||||
{!excludeFilters && (
|
||||
<>
|
||||
<Tab id="segments">{formatMessage(labels.segments)}</Tab>
|
||||
<Tab id="cohorts">{formatMessage(labels.cohorts)}</Tab>
|
||||
</>
|
||||
)}
|
||||
</TabList>
|
||||
<TabPanel id="fields">
|
||||
<FieldFilters
|
||||
websiteId={websiteId}
|
||||
value={currentFilters}
|
||||
onChange={setCurrentFilters}
|
||||
exclude={excludeFilters ? ['path', 'title', 'hostname', 'tag', 'event'] : []}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel id="segments">
|
||||
<SegmentFilters
|
||||
websiteId={websiteId}
|
||||
segmentId={currentSegment}
|
||||
onChange={handleSegmentChange}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel id="cohorts">
|
||||
<SegmentFilters
|
||||
type="cohort"
|
||||
websiteId={websiteId}
|
||||
segmentId={currentCohort}
|
||||
onChange={handleSegmentChange}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</Column>
|
||||
<Row alignItems="center" justifyContent="space-between" gap>
|
||||
<Button onPress={handleReset}>{formatMessage(labels.reset)}</Button>
|
||||
<Row alignItems="center" justifyContent="flex-end" gridColumn="span 2" gap>
|
||||
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
Column,
|
||||
Pressable,
|
||||
IconLabel,
|
||||
useBreakpoint,
|
||||
} from '@umami/react-zen';
|
||||
import { useConfig, useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
|
||||
import {
|
||||
|
|
@ -39,9 +40,11 @@ export function NavButton({ showText = true }: TeamsButtonProps) {
|
|||
const { cloudMode } = useConfig();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { teamId } = useNavigation();
|
||||
const breakpoint = useBreakpoint();
|
||||
const team = user?.teams?.find(({ id }) => id === teamId);
|
||||
const selectedKeys = new Set([teamId || 'user']);
|
||||
const label = teamId ? team?.name : user.username;
|
||||
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
|
||||
|
||||
const getUrl = (url: string) => {
|
||||
return cloudMode ? `${process.env.cloudUrl}${url}` : url;
|
||||
|
|
@ -82,7 +85,7 @@ export function NavButton({ showText = true }: TeamsButtonProps) {
|
|||
<MenuItem id="teams" showChecked={false} showSubMenuIcon>
|
||||
<IconLabel icon={<Switch />} label={formatMessage(labels.switchAccount)} />
|
||||
</MenuItem>
|
||||
<Popover placement="right top">
|
||||
<Popover placement={isMobile ? 'bottom start' : 'right top'}>
|
||||
<Column minWidth="300px">
|
||||
<Menu selectionMode="single" selectedKeys={selectedKeys}>
|
||||
<MenuSection title={formatMessage(labels.myAccount)}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, Icon, DialogTrigger, Dialog, Popover, Text } from '@umami/react-zen';
|
||||
import { Button, Icon, DialogTrigger, Dialog, Text, Modal, useBreakpoint } from '@umami/react-zen';
|
||||
import { ListFilter } from '@/components/icons';
|
||||
import { FilterEditForm } from '@/components/input/FilterEditForm';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
|
|
@ -15,6 +15,8 @@ export function WebsiteFilterButton({
|
|||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { updateParams, router } = useNavigation();
|
||||
const breakpoint = useBreakpoint();
|
||||
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
|
||||
|
||||
const handleChange = ({ filters, segment, cohort }: any) => {
|
||||
const params = filtersArrayToObject(filters);
|
||||
|
|
@ -32,13 +34,13 @@ export function WebsiteFilterButton({
|
|||
</Icon>
|
||||
{showText && <Text>{formatMessage(labels.filter)}</Text>}
|
||||
</Button>
|
||||
<Popover placement="bottom start">
|
||||
<Dialog title={formatMessage(labels.filters)} style={{ width: 800, minHeight: 600 }}>
|
||||
<Modal position={isMobile ? 'fullscreen' : 'center'}>
|
||||
<Dialog title={formatMessage(labels.filters)}>
|
||||
{({ close }) => {
|
||||
return <FilterEditForm websiteId={websiteId} onChange={handleChange} onClose={close} />;
|
||||
}}
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</Modal>
|
||||
</DialogTrigger>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue