ui: start event analytics
This commit is contained in:
parent
6360b9a580
commit
6097af4839
12 changed files with 330 additions and 35 deletions
|
|
@ -37,6 +37,7 @@ const components: any = {
|
|||
SpotPure: lazy(() => import('Components/Spots/SpotPlayer')),
|
||||
ScopeSetup: lazy(() => import('Components/ScopeForm')),
|
||||
HighlightsPure: lazy(() => import('Components/Highlights/HighlightsList')),
|
||||
ActivityPure: lazy(() => import('Components/DataManagement/Activity/Page')),
|
||||
};
|
||||
|
||||
const enhancedComponents: any = {
|
||||
|
|
@ -60,6 +61,7 @@ const enhancedComponents: any = {
|
|||
Spot: components.SpotPure,
|
||||
ScopeSetup: components.ScopeSetup,
|
||||
Highlights: components.HighlightsPure,
|
||||
Activity: components.ActivityPure,
|
||||
};
|
||||
|
||||
const withSiteId = routes.withSiteId;
|
||||
|
|
@ -109,6 +111,10 @@ const SCOPE_SETUP = routes.scopeSetup();
|
|||
|
||||
const HIGHLIGHTS_PATH = routes.highlights();
|
||||
|
||||
const DATA_MANAGEMENT = {
|
||||
ACTIVITY: routes.dataManagement.activity()
|
||||
}
|
||||
|
||||
function PrivateRoutes() {
|
||||
const { projectsStore, userStore, integrationsStore } = useStore();
|
||||
const onboarding = userStore.onboarding;
|
||||
|
|
@ -290,6 +296,12 @@ function PrivateRoutes() {
|
|||
path={withSiteId(LIVE_SESSION_PATH, siteIdList)}
|
||||
component={enhancedComponents.LiveSession}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(DATA_MANAGEMENT.ACTIVITY, siteIdList)}
|
||||
component={enhancedComponents.Activity}
|
||||
/>
|
||||
{Object.entries(routes.redirects).map(([fr, to]) => (
|
||||
<Redirect key={fr} exact strict from={fr} to={to} />
|
||||
))}
|
||||
|
|
|
|||
182
frontend/app/components/DataManagement/Activity/Page.tsx
Normal file
182
frontend/app/components/DataManagement/Activity/Page.tsx
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import React from 'react';
|
||||
import { EventsList, FilterList } from 'Shared/Filters/FilterList';
|
||||
import { Table, Dropdown } from 'antd';
|
||||
import { MoreOutlined } from '@ant-design/icons';
|
||||
import { numberWithCommas } from 'App/utils';
|
||||
import { Pagination } from 'UI';
|
||||
import Event from './data/Event';
|
||||
|
||||
function ActivityPage() {
|
||||
const [hiddenCols, setHiddenCols] = React.useState([]);
|
||||
const appliedFilter = { filters: [] };
|
||||
const onAddFilter = () => {};
|
||||
const onUpdateFilter = () => {};
|
||||
const onRemoveFilter = () => {};
|
||||
const onChangeEventsOrder = () => {};
|
||||
const saveRequestPayloads = () => {};
|
||||
const onFilterMove = () => {};
|
||||
const [editCols, setEditCols] = React.useState(false);
|
||||
|
||||
const dropdownItems = [
|
||||
{
|
||||
label: 'Show/Hide Columns',
|
||||
key: 'edit-columns',
|
||||
onClick: () => setEditCols(true),
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Event Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, row) => (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{row.$_isAutoCapture && (
|
||||
<span className={'text-gray-500'}>[auto]</span>
|
||||
)}
|
||||
<span>{row.name}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Time',
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.time - b.time,
|
||||
},
|
||||
{
|
||||
title: 'Distinct ID',
|
||||
dataIndex: 'userId',
|
||||
key: 'userId',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.userId.localeCompare(b.userId),
|
||||
render: (text) => <div className={'link'}>{text}</div>,
|
||||
},
|
||||
{
|
||||
title: 'City',
|
||||
dataIndex: 'userCity',
|
||||
key: 'userCity',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.userCiry.localeCompare(b.userCity),
|
||||
},
|
||||
{
|
||||
title: 'Environment',
|
||||
dataIndex: 'userEnvironment',
|
||||
key: 'userEnvironment',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.userEnvironment.localeCompare(b.userEnvironment),
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<Dropdown menu={{ items: dropdownItems }} trigger={'click'}>
|
||||
<div className={'cursor-pointer'}>
|
||||
<MoreOutlined />
|
||||
</div>
|
||||
</Dropdown>
|
||||
),
|
||||
dataIndex: '$__opts__$',
|
||||
key: '$__opts__$',
|
||||
width: 50,
|
||||
},
|
||||
];
|
||||
|
||||
const shownCols = columns.map((col) => ({
|
||||
...col,
|
||||
hidden: hiddenCols.includes(col.key),
|
||||
}));
|
||||
|
||||
const page = 1;
|
||||
const limit = 100;
|
||||
const total = 3000;
|
||||
const testEv = new Event(
|
||||
'test ev',
|
||||
Date.now(),
|
||||
{ userId: '123', userCity: 'NY', userEnvironment: 'Mac OS' },
|
||||
{},
|
||||
false
|
||||
);
|
||||
const testAutoEv = new Event(
|
||||
'test auto ev',
|
||||
Date.now(),
|
||||
{ userId: '123', userCity: 'NY', userEnvironment: 'Mac OS' },
|
||||
{},
|
||||
true
|
||||
);
|
||||
const list = [testEv.toData(), testAutoEv.toData()];
|
||||
const onPageChange = () => {};
|
||||
return (
|
||||
<div
|
||||
className={'flex flex-col gap-2'}
|
||||
style={{ maxWidth: '1360px', margin: 'auto' }}
|
||||
>
|
||||
<div className={'shadow rounded-xl'}>
|
||||
<EventsList
|
||||
filter={appliedFilter}
|
||||
onAddFilter={onAddFilter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
onChangeEventsOrder={onChangeEventsOrder}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
onFilterMove={onFilterMove}
|
||||
mergeDown
|
||||
heading={
|
||||
<div
|
||||
className={
|
||||
'-mx-4 px-4 border-b w-full py-2 font-semibold text-lg'
|
||||
}
|
||||
style={{ width: 'calc(100% + 2rem)' }}
|
||||
>
|
||||
Activity
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<FilterList
|
||||
mergeUp
|
||||
filter={appliedFilter}
|
||||
onAddFilter={onAddFilter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
onChangeEventsOrder={onChangeEventsOrder}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
onFilterMove={onFilterMove}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
'bg-white rounded-xl shadow border flex flex-col overflow-hidden'
|
||||
}
|
||||
>
|
||||
<div className={'px-4 py-2 font-semibold text-lg'}>
|
||||
All users activity
|
||||
</div>
|
||||
<Table dataSource={list} pagination={false} columns={shownCols} />
|
||||
<div className="flex items-center justify-between px-4 py-3 shadow-sm w-full bg-white rounded-lg mt-2">
|
||||
<div>
|
||||
{'Showing '}
|
||||
<span className="font-medium">{(page - 1) * limit + 1}</span>
|
||||
{' to '}
|
||||
<span className="font-medium">
|
||||
{(page - 1) * limit + list.length}
|
||||
</span>
|
||||
{' of '}
|
||||
<span className="font-medium">{numberWithCommas(total)}</span>
|
||||
{' events.'}
|
||||
</div>
|
||||
<Pagination
|
||||
page={page}
|
||||
total={total}
|
||||
onPageChange={onPageChange}
|
||||
limit={limit}
|
||||
debounceRequest={500}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ActivityPage;
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
interface DefaultFields {
|
||||
userId: string;
|
||||
userCity: string;
|
||||
userEnvironment: string;
|
||||
}
|
||||
|
||||
export default class Event {
|
||||
name: string;
|
||||
time: string;
|
||||
defaultFields: DefaultFields = {
|
||||
userId: '',
|
||||
userCity: '',
|
||||
userEnvironment: '',
|
||||
}
|
||||
customFields?: Record<string,any> = undefined;
|
||||
|
||||
readonly $_isAutoCapture;
|
||||
|
||||
constructor(name: string, time: string, defaultFields: DefaultFields, customFields?: Record<string, any>, isAutoCapture = false) {
|
||||
this.name = name;
|
||||
this.time = time;
|
||||
this.defaultFields = defaultFields;
|
||||
this.customFields = customFields;
|
||||
this.$_isAutoCapture = isAutoCapture;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const obj = this.toData();
|
||||
return JSON.stringify(obj, 4);
|
||||
}
|
||||
|
||||
toData() {
|
||||
const obj: any = {
|
||||
name: this.name,
|
||||
time: this.time,
|
||||
$_isAutoCapture: this.$_isAutoCapture,
|
||||
}
|
||||
Object.entries(this.defaultFields).forEach(([key, value]) => {
|
||||
obj[key] = value;
|
||||
});
|
||||
if (this.customFields) {
|
||||
Object.entries(this.customFields).forEach(([key, value]) => {
|
||||
obj[key] = value;
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ function FunnelTable(props: Props) {
|
|||
if (props.compData) {
|
||||
tableData.push({
|
||||
conversion: props.compData.funnel.totalConversionsPercentage,
|
||||
})
|
||||
});
|
||||
const compFunnel = props.compData.funnel;
|
||||
compFunnel.stages.forEach((st, ind) => {
|
||||
tableData[1]['st_' + ind] = st.count;
|
||||
|
|
@ -71,9 +71,7 @@ function FunnelTable(props: Props) {
|
|||
pagination={false}
|
||||
size={'middle'}
|
||||
scroll={{ x: 'max-content' }}
|
||||
rowClassName={(_, index) => (
|
||||
index > 0 ? 'opacity-70' : ''
|
||||
)}
|
||||
rowClassName={(_, index) => (index > 0 ? 'opacity-70' : '')}
|
||||
/>
|
||||
<TableExporter
|
||||
tableColumns={tableProps}
|
||||
|
|
@ -101,7 +99,6 @@ export function TableExporter({
|
|||
}) {
|
||||
const onClick = () => exportAntCsv(tableColumns, tableData, filename);
|
||||
return (
|
||||
<Tooltip title='Export Data to CSV'>
|
||||
<div
|
||||
className={`absolute ${top ? top : 'top-0'} ${
|
||||
right ? right : '-right-1'
|
||||
|
|
@ -111,17 +108,16 @@ export function TableExporter({
|
|||
items={[{ icon: 'download', text: 'Export to CSV', onClick }]}
|
||||
bold
|
||||
customTrigger={
|
||||
<div
|
||||
className={
|
||||
'flex items-center justify-center bg-gradient-to-r from-[#fafafa] to-neutral-200 cursor-pointer rounded-lg h-[38px] w-[38px] btn-export-table-data'
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
'flex items-center justify-center bg-gradient-to-r from-[#fafafa] to-neutral-200 cursor-pointer rounded-lg h-[38px] w-[38px] btn-export-table-data'
|
||||
}
|
||||
>
|
||||
<EllipsisVertical size={16} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GripVertical, Plus, Filter } from 'lucide-react';
|
||||
import { GripVertical, Plus, Filter, SquareDashedMousePointer } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Button } from 'antd';
|
||||
|
|
@ -27,6 +27,8 @@ interface Props {
|
|||
mergeUp?: boolean;
|
||||
borderless?: boolean;
|
||||
cannotAdd?: boolean;
|
||||
heading?: React.ReactNode;
|
||||
isLive?: boolean;
|
||||
}
|
||||
|
||||
export const FilterList = observer((props: Props) => {
|
||||
|
|
@ -39,7 +41,8 @@ export const FilterList = observer((props: Props) => {
|
|||
onAddFilter,
|
||||
readonly,
|
||||
borderless,
|
||||
excludeCategory
|
||||
excludeCategory,
|
||||
isLive
|
||||
} = props;
|
||||
|
||||
const filters = filter.filters;
|
||||
|
|
@ -70,6 +73,7 @@ export const FilterList = observer((props: Props) => {
|
|||
disabled={readonly}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
excludeCategory={excludeCategory}
|
||||
isLive={isLive}
|
||||
>
|
||||
<Button
|
||||
icon={<Filter size={16} strokeWidth={1} />}
|
||||
|
|
@ -97,6 +101,7 @@ export const FilterList = observer((props: Props) => {
|
|||
isFilter={true}
|
||||
filterIndex={filterIndex}
|
||||
filter={filter}
|
||||
isLive={isLive}
|
||||
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
|
|
@ -213,9 +218,10 @@ export const EventsList = observer((props: Props) => {
|
|||
marginBottom: props.mergeDown ? '-1px' : undefined
|
||||
}}
|
||||
>
|
||||
{props.heading ? props.heading : null}
|
||||
<div className="flex items-center mb-2 gap-2">
|
||||
<div className="font-medium">Events</div>
|
||||
{cannotAdd ? null : (
|
||||
{filters.length === 0 || cannotAdd ? null : (
|
||||
<FilterSelection
|
||||
mode={'events'}
|
||||
filter={undefined}
|
||||
|
|
@ -224,12 +230,12 @@ export const EventsList = observer((props: Props) => {
|
|||
excludeCategory={excludeCategory}
|
||||
>
|
||||
<Button
|
||||
icon={<Plus size={16} strokeWidth={1} />}
|
||||
icon={<SquareDashedMousePointer size={16} strokeWidth={1} />}
|
||||
type="default"
|
||||
size={'small'}
|
||||
className="btn-add-event"
|
||||
>
|
||||
Add
|
||||
Select Event
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
)}
|
||||
|
|
@ -243,6 +249,28 @@ export const EventsList = observer((props: Props) => {
|
|||
</div>
|
||||
</div>
|
||||
<div className={'flex flex-col '}>
|
||||
{filters.length === 0 ? (
|
||||
<div className={'flex items-center gap-2 mb-2'}>
|
||||
<div className={'bg-gray-lighter rounded-full leading-none flex items-center justify-center w-5 h-5'}>
|
||||
<div>1</div>
|
||||
</div>
|
||||
<FilterSelection
|
||||
mode={'events'}
|
||||
filter={undefined}
|
||||
onFilterClick={onAddFilter}
|
||||
excludeCategory={excludeCategory}
|
||||
>
|
||||
<Button
|
||||
icon={<SquareDashedMousePointer size={16} strokeWidth={1} />}
|
||||
type="default"
|
||||
size={'small'}
|
||||
className='btn-add-event'
|
||||
>
|
||||
Select Event
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
</div>
|
||||
) : null}
|
||||
{filters.map((filter: any, filterIndex: number) =>
|
||||
filter.isEvent ? (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface Props {
|
|||
function Square_mouse_pointer(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" width={ `${ width }px` } height={ `${ height }px` } ><path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/><path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" width={ `${ width }px` } height={ `${ height }px` } ><path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/><path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6" fill="none"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ function SideMenu(props: Props) {
|
|||
[PREFERENCES_MENU.BILLING]: () => client(CLIENT_TABS.BILLING),
|
||||
[PREFERENCES_MENU.MODULES]: () => client(CLIENT_TABS.MODULES),
|
||||
[MENU.HIGHLIGHTS]: () => withSiteId(routes.highlights(''), siteId),
|
||||
[MENU.ACTIVITY]: () => withSiteId(routes.dataManagement.activity(), siteId),
|
||||
};
|
||||
|
||||
const handleClick = (item: any) => {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ export const enum MENU {
|
|||
SUPPORT = 'support',
|
||||
EXIT = 'exit',
|
||||
SPOTS = 'spots',
|
||||
ACTIVITY = 'activity',
|
||||
}
|
||||
|
||||
export const categories: Category[] = [
|
||||
|
|
@ -61,26 +62,27 @@ export const categories: Category[] = [
|
|||
key: 'replays',
|
||||
items: [
|
||||
{ label: 'Sessions', key: MENU.SESSIONS, icon: 'collection-play' },
|
||||
{ label: 'Recommendations', key: MENU.RECOMMENDATIONS, icon: 'magic', hidden: true },
|
||||
{
|
||||
label: 'Recommendations',
|
||||
key: MENU.RECOMMENDATIONS,
|
||||
icon: 'magic',
|
||||
hidden: true,
|
||||
},
|
||||
{ label: 'Vault', key: MENU.VAULT, icon: 'safe', hidden: true },
|
||||
{ label: 'Bookmarks', key: MENU.BOOKMARKS, icon: 'bookmark' },
|
||||
//{ label: 'Notes', key: MENU.NOTES, icon: 'stickies' },
|
||||
{ label: 'Highlights', key: MENU.HIGHLIGHTS, icon: 'chat-square-quote' }
|
||||
]
|
||||
{ label: 'Highlights', key: MENU.HIGHLIGHTS, icon: 'chat-square-quote' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'spot',
|
||||
items: [
|
||||
{ label: 'Spots', key: MENU.SPOTS, icon: 'orspotOutline' },
|
||||
]
|
||||
items: [{ label: 'Spots', key: MENU.SPOTS, icon: 'orspotOutline' }],
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'assist',
|
||||
items: [
|
||||
{ label: 'Co-Browse', key: MENU.LIVE_SESSIONS, icon: 'broadcast' },
|
||||
]
|
||||
items: [{ label: 'Co-Browse', key: MENU.LIVE_SESSIONS, icon: 'broadcast' }],
|
||||
},
|
||||
{
|
||||
title: 'Analytics',
|
||||
|
|
@ -96,25 +98,39 @@ export const categories: Category[] = [
|
|||
// { label: 'Resource Monitoring', key: MENU.RESOURCE_MONITORING }
|
||||
// ]
|
||||
// },
|
||||
{ label: 'Alerts', key: MENU.ALERTS, icon: 'bell' }
|
||||
]
|
||||
{ label: 'Alerts', key: MENU.ALERTS, icon: 'bell' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Data Management',
|
||||
key: 'data-management',
|
||||
items: [{ label: 'Activity', key: MENU.ACTIVITY, icon: 'square-mouse-pointer' }],
|
||||
},
|
||||
{
|
||||
title: 'Product Optimization',
|
||||
key: 'product-optimization',
|
||||
items: [
|
||||
{ label: 'Feature Flags', key: MENU.FEATURE_FLAGS, icon: 'toggles' },
|
||||
{ label: 'Usability Tests', key: MENU.USABILITY_TESTS, icon: 'clipboard-check' },
|
||||
]
|
||||
{
|
||||
label: 'Usability Tests',
|
||||
key: MENU.USABILITY_TESTS,
|
||||
icon: 'clipboard-check',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
key: 'other',
|
||||
items: [
|
||||
{ label: 'Preferences', key: MENU.PREFERENCES, icon: 'sliders', leading: 'chevron-right' },
|
||||
{ label: 'Support', key: MENU.SUPPORT, icon: 'question-circle' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Preferences',
|
||||
key: MENU.PREFERENCES,
|
||||
icon: 'sliders',
|
||||
leading: 'chevron-right',
|
||||
},
|
||||
{ label: 'Support', key: MENU.SUPPORT, icon: 'question-circle' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const preferences: Category[] = [
|
||||
|
|
|
|||
|
|
@ -148,6 +148,10 @@ export const scopeSetup = (): string => '/scope-setup';
|
|||
|
||||
export const highlights = (): string => '/highlights';
|
||||
|
||||
export const dataManagement = {
|
||||
activity: () => '/data-management/activity',
|
||||
}
|
||||
|
||||
const REQUIRED_SITE_ID_ROUTES = [
|
||||
liveSession(''),
|
||||
session(''),
|
||||
|
|
|
|||
|
|
@ -403,4 +403,8 @@ svg {
|
|||
background-color: #c6c6c6;
|
||||
border-radius: 4px;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.ant-table-column-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
@ -1 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-mouse-pointer"><path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/><path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-mouse-pointer">
|
||||
<path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/>
|
||||
<path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6" stroke="no-fill"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 450 B |
|
|
@ -116,6 +116,7 @@ function ${titleCase(fileName)}(props: Props) {
|
|||
/clipRule="evenoddCustomFill"/g,
|
||||
'clipRule="evenodd" fillRule="evenodd"'
|
||||
)
|
||||
.replaceAll(`stroke="no-fill"`, 'fill="none"')
|
||||
.replaceAll(/fill-rule/g, 'fillRule')
|
||||
.replaceAll(/fill-opacity/g, 'fillOpacity')
|
||||
.replaceAll(/stop-color/g, 'stopColor')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue