Spaces:
Paused
Paused
Download docs/src/components/ui-components/ComponentIndex.tsx from Leon4gr45/powerbi: direct link, hf CLI and curl.
- Browser
- Download file 7.81 kB
-
https://huggingface.co/spaces/Leon4gr45/powerbi/resolve/main/docs/src/components/ui-components/ComponentIndex.tsx
- Command line
-
hf download hf://spaces/Leon4gr45/powerbi/docs/src/components/ui-components/ComponentIndex.tsx
-
curl -L -o ComponentIndex.tsx https://huggingface.co/spaces/Leon4gr45/powerbi/resolve/main/docs/src/components/ui-components/ComponentIndex.tsx
7.81 kB
| /** | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| import React, { useState, useMemo } from 'react'; | |
| import { Card, Row, Col, Statistic, Table, Tag, Input, Select } from 'antd'; | |
| import { | |
| AppstoreOutlined, | |
| ApiOutlined, | |
| PictureOutlined, | |
| PlayCircleOutlined, | |
| SearchOutlined, | |
| } from '@ant-design/icons'; | |
| import type { ComponentData, ComponentEntry } from './types'; | |
| interface ComponentIndexProps { | |
| data: ComponentData; | |
| } | |
| const CATEGORY_COLORS: Record<string, string> = { | |
| ui: 'blue', | |
| 'design-system': 'green', | |
| extension: 'purple', | |
| }; | |
| const CATEGORY_LABELS: Record<string, string> = { | |
| ui: 'Core', | |
| 'design-system': 'Layout', | |
| extension: 'Extension', | |
| }; | |
| const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => { | |
| const [searchText, setSearchText] = useState(''); | |
| const [categoryFilter, setCategoryFilter] = useState<string | null>(null); | |
| const { statistics, components } = data; | |
| const filteredComponents = useMemo(() => { | |
| return components | |
| .filter(comp => { | |
| const matchesSearch = | |
| !searchText || | |
| comp.name.toLowerCase().includes(searchText.toLowerCase()) || | |
| comp.description.toLowerCase().includes(searchText.toLowerCase()) || | |
| comp.package.toLowerCase().includes(searchText.toLowerCase()); | |
| const matchesCategory = | |
| !categoryFilter || comp.category === categoryFilter; | |
| return matchesSearch && matchesCategory; | |
| }) | |
| .sort((a, b) => a.name.localeCompare(b.name)); | |
| }, [components, searchText, categoryFilter]); | |
| const { categories, categoryCounts } = useMemo(() => { | |
| const counts: Record<string, number> = {}; | |
| components.forEach(comp => { | |
| counts[comp.category] = (counts[comp.category] || 0) + 1; | |
| }); | |
| return { | |
| categories: Object.keys(counts).sort(), | |
| categoryCounts: counts, | |
| }; | |
| }, [components]); | |
| const columns = [ | |
| { | |
| title: 'Component', | |
| dataIndex: 'name', | |
| key: 'name', | |
| sorter: (a: ComponentEntry, b: ComponentEntry) => | |
| a.name.localeCompare(b.name), | |
| defaultSortOrder: 'ascend' as const, | |
| render: (name: string, record: ComponentEntry) => ( | |
| <div> | |
| <a href={`/${record.docPath}`}> | |
| <strong>{name}</strong> | |
| </a> | |
| {record.description && ( | |
| <div style={{ fontSize: '12px', color: '#666' }}> | |
| {record.description.slice(0, 100)} | |
| {record.description.length > 100 ? '...' : ''} | |
| </div> | |
| )} | |
| </div> | |
| ), | |
| }, | |
| { | |
| title: 'Category', | |
| dataIndex: 'category', | |
| key: 'category', | |
| width: 120, | |
| filters: categories.map(cat => ({ | |
| text: CATEGORY_LABELS[cat] || cat, | |
| value: cat, | |
| })), | |
| onFilter: (value: React.Key | boolean, record: ComponentEntry) => | |
| record.category === value, | |
| render: (cat: string) => ( | |
| <Tag color={CATEGORY_COLORS[cat] || 'default'}> | |
| {CATEGORY_LABELS[cat] || cat} | |
| </Tag> | |
| ), | |
| }, | |
| { | |
| title: 'Package', | |
| dataIndex: 'package', | |
| key: 'package', | |
| width: 220, | |
| render: (pkg: string) => <code style={{ fontSize: '12px' }}>{pkg}</code>, | |
| }, | |
| { | |
| title: 'Tags', | |
| key: 'tags', | |
| width: 280, | |
| filters: [ | |
| { text: 'Extension Compatible', value: 'extension' }, | |
| { text: 'Gallery', value: 'gallery' }, | |
| { text: 'Live Demo', value: 'demo' }, | |
| ], | |
| onFilter: (value: React.Key | boolean, record: ComponentEntry) => { | |
| switch (value) { | |
| case 'extension': | |
| return record.extensionCompatible; | |
| case 'gallery': | |
| return record.hasGallery; | |
| case 'demo': | |
| return record.hasLiveExample; | |
| default: | |
| return true; | |
| } | |
| }, | |
| render: (_: unknown, record: ComponentEntry) => ( | |
| <div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}> | |
| {record.extensionCompatible && ( | |
| <Tag color="purple">Extension Compatible</Tag> | |
| )} | |
| {record.hasGallery && <Tag color="cyan">Gallery</Tag>} | |
| {record.hasLiveExample && <Tag color="green">Demo</Tag>} | |
| </div> | |
| ), | |
| }, | |
| { | |
| title: 'Props', | |
| dataIndex: 'propsCount', | |
| key: 'propsCount', | |
| width: 80, | |
| sorter: (a: ComponentEntry, b: ComponentEntry) => | |
| a.propsCount - b.propsCount, | |
| render: (count: number) => ( | |
| <span style={{ color: count > 0 ? '#1890ff' : '#999' }}>{count}</span> | |
| ), | |
| }, | |
| ]; | |
| return ( | |
| <div className="component-index"> | |
| <Row gutter={[16, 16]} style={{ marginBottom: 24 }}> | |
| <Col xs={12} sm={6}> | |
| <Card> | |
| <Statistic | |
| title="Total Components" | |
| value={statistics.totalComponents} | |
| prefix={<AppstoreOutlined />} | |
| /> | |
| </Card> | |
| </Col> | |
| <Col xs={12} sm={6}> | |
| <Card> | |
| <Statistic | |
| title="Extension Compatible" | |
| value={statistics.extensionCompatible} | |
| prefix={<ApiOutlined />} | |
| /> | |
| </Card> | |
| </Col> | |
| <Col xs={12} sm={6}> | |
| <Card> | |
| <Statistic | |
| title="With Gallery" | |
| value={statistics.withGallery} | |
| prefix={<PictureOutlined />} | |
| /> | |
| </Card> | |
| </Col> | |
| <Col xs={12} sm={6}> | |
| <Card> | |
| <Statistic | |
| title="With Live Demo" | |
| value={statistics.withLiveExample} | |
| prefix={<PlayCircleOutlined />} | |
| /> | |
| </Card> | |
| </Col> | |
| </Row> | |
| <Row gutter={[16, 16]} style={{ marginBottom: 16 }}> | |
| <Col xs={24} sm={12}> | |
| <Input | |
| placeholder="Search components..." | |
| prefix={<SearchOutlined />} | |
| value={searchText} | |
| onChange={e => setSearchText(e.target.value)} | |
| allowClear | |
| /> | |
| </Col> | |
| <Col xs={24} sm={12}> | |
| <Select | |
| placeholder="Filter by category" | |
| style={{ width: '100%' }} | |
| value={categoryFilter} | |
| onChange={setCategoryFilter} | |
| allowClear | |
| options={categories.map(cat => ({ | |
| label: ( | |
| <span> | |
| <Tag | |
| color={CATEGORY_COLORS[cat] || 'default'} | |
| style={{ marginRight: 8 }} | |
| > | |
| {categoryCounts[cat] || 0} | |
| </Tag> | |
| {CATEGORY_LABELS[cat] || cat} | |
| </span> | |
| ), | |
| value: cat, | |
| }))} | |
| /> | |
| </Col> | |
| </Row> | |
| <Table | |
| dataSource={filteredComponents} | |
| columns={columns} | |
| rowKey="name" | |
| pagination={{ | |
| defaultPageSize: 20, | |
| showSizeChanger: true, | |
| showTotal: total => `${total} components`, | |
| }} | |
| size="middle" | |
| /> | |
| </div> | |
| ); | |
| }; | |
| export default ComponentIndex; | |