mirror of
https://github.com/apache/superset.git
synced 2026-04-14 05:34:38 +00:00
refactor(monorepo): move superset-ui to superset(stage 2) (#17552)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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, { ComponentType, ChangeEventHandler } from 'react';
|
||||
import { Row, FilterValue } from 'react-table';
|
||||
import useAsyncState from '../utils/useAsyncState';
|
||||
|
||||
export interface SearchInputProps {
|
||||
count: number;
|
||||
value: string;
|
||||
onChange: ChangeEventHandler<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export interface GlobalFilterProps<D extends object> {
|
||||
preGlobalFilteredRows: Row<D>[];
|
||||
// filter value cannot be `undefined` otherwise React will report component
|
||||
// control type undefined error
|
||||
filterValue: string;
|
||||
setGlobalFilter: (filterValue: FilterValue) => void;
|
||||
searchInput?: ComponentType<SearchInputProps>;
|
||||
}
|
||||
|
||||
function DefaultSearchInput({ count, value, onChange }: SearchInputProps) {
|
||||
return (
|
||||
<span className="dt-global-filter">
|
||||
Search{' '}
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
placeholder={`${count} records...`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default (React.memo as <T>(fn: T) => T)(function GlobalFilter<
|
||||
D extends object,
|
||||
>({
|
||||
preGlobalFilteredRows,
|
||||
filterValue = '',
|
||||
searchInput,
|
||||
setGlobalFilter,
|
||||
}: GlobalFilterProps<D>) {
|
||||
const count = preGlobalFilteredRows.length;
|
||||
const [value, setValue] = useAsyncState(
|
||||
filterValue,
|
||||
(newValue: string) => {
|
||||
setGlobalFilter(newValue || undefined);
|
||||
},
|
||||
200,
|
||||
);
|
||||
|
||||
const SearchInput = searchInput || DefaultSearchInput;
|
||||
|
||||
return (
|
||||
<SearchInput
|
||||
count={count}
|
||||
value={value}
|
||||
onChange={e => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
e.preventDefault();
|
||||
setValue(target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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, { CSSProperties } from 'react';
|
||||
|
||||
export interface PaginationProps {
|
||||
pageCount: number; // number of pages
|
||||
currentPage?: number; // index of current page, zero-based
|
||||
maxPageItemCount?: number;
|
||||
ellipsis?: string; // content for ellipsis item
|
||||
onPageChange: (page: number) => void; // `page` is zero-based
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
// first, ..., prev, current, next, ..., last
|
||||
const MINIMAL_PAGE_ITEM_COUNT = 7;
|
||||
|
||||
/**
|
||||
* Generate numeric page items around current page.
|
||||
* - Always include first and last page
|
||||
* - Add ellipsis if needed
|
||||
*/
|
||||
export function generatePageItems(
|
||||
total: number,
|
||||
current: number,
|
||||
width: number,
|
||||
) {
|
||||
if (width < MINIMAL_PAGE_ITEM_COUNT) {
|
||||
throw new Error(
|
||||
`Must allow at least ${MINIMAL_PAGE_ITEM_COUNT} page items`,
|
||||
);
|
||||
}
|
||||
if (width % 2 === 0) {
|
||||
throw new Error(`Must allow odd number of page items`);
|
||||
}
|
||||
if (total < width) {
|
||||
return [...new Array(total).keys()];
|
||||
}
|
||||
const left = Math.max(
|
||||
0,
|
||||
Math.min(total - width, current - Math.floor(width / 2)),
|
||||
);
|
||||
const items: (string | number)[] = new Array(width);
|
||||
for (let i = 0; i < width; i += 1) {
|
||||
items[i] = i + left;
|
||||
}
|
||||
// replace non-ending items with placeholders
|
||||
if (items[0] > 0) {
|
||||
items[0] = 0;
|
||||
items[1] = 'prev-more';
|
||||
}
|
||||
if (items[items.length - 1] < total - 1) {
|
||||
items[items.length - 1] = total - 1;
|
||||
items[items.length - 2] = 'next-more';
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
export default React.memo(
|
||||
React.forwardRef(function Pagination(
|
||||
{
|
||||
style,
|
||||
pageCount,
|
||||
currentPage = 0,
|
||||
maxPageItemCount = 9,
|
||||
onPageChange,
|
||||
}: PaginationProps,
|
||||
ref: React.Ref<HTMLDivElement>,
|
||||
) {
|
||||
const pageItems = generatePageItems(
|
||||
pageCount,
|
||||
currentPage,
|
||||
maxPageItemCount,
|
||||
);
|
||||
return (
|
||||
<div ref={ref} className="dt-pagination" style={style}>
|
||||
<ul className="pagination pagination-sm">
|
||||
{pageItems.map(item =>
|
||||
typeof item === 'number' ? (
|
||||
// actual page number
|
||||
<li
|
||||
key={item}
|
||||
className={currentPage === item ? 'active' : undefined}
|
||||
>
|
||||
<a
|
||||
href={`#page-${item}`}
|
||||
role="button"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
onPageChange(item);
|
||||
}}
|
||||
>
|
||||
{item + 1}
|
||||
</a>
|
||||
</li>
|
||||
) : (
|
||||
<li key={item} className="dt-pagination-ellipsis">
|
||||
<span>…</span>
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import { formatSelectOptions } from '@superset-ui/chart-controls';
|
||||
|
||||
export type SizeOption = [number, string];
|
||||
|
||||
export interface SelectPageSizeRendererProps {
|
||||
current: number;
|
||||
options: SizeOption[];
|
||||
onChange: SelectPageSizeProps['onChange'];
|
||||
}
|
||||
|
||||
function DefaultSelectRenderer({
|
||||
current,
|
||||
options,
|
||||
onChange,
|
||||
}: SelectPageSizeRendererProps) {
|
||||
return (
|
||||
<span className="dt-select-page-size form-inline">
|
||||
Show{' '}
|
||||
<select
|
||||
className="form-control input-sm"
|
||||
value={current}
|
||||
onBlur={() => {}}
|
||||
onChange={e => {
|
||||
onChange(Number((e.target as HTMLSelectElement).value));
|
||||
}}
|
||||
>
|
||||
{options.map(option => {
|
||||
const [size, text] = Array.isArray(option)
|
||||
? option
|
||||
: [option, option];
|
||||
return (
|
||||
<option key={size} value={size}>
|
||||
{text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>{' '}
|
||||
entries
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export interface SelectPageSizeProps extends SelectPageSizeRendererProps {
|
||||
total?: number;
|
||||
selectRenderer?: typeof DefaultSelectRenderer;
|
||||
onChange: (pageSize: number) => void;
|
||||
}
|
||||
|
||||
function getOptionValue(x: SizeOption) {
|
||||
return Array.isArray(x) ? x[0] : x;
|
||||
}
|
||||
|
||||
export default React.memo(function SelectPageSize({
|
||||
total,
|
||||
options: sizeOptions,
|
||||
current: currentSize,
|
||||
selectRenderer,
|
||||
onChange,
|
||||
}: SelectPageSizeProps) {
|
||||
const sizeOptionValues = sizeOptions.map(getOptionValue);
|
||||
let options = [...sizeOptions];
|
||||
// insert current size to list
|
||||
if (
|
||||
currentSize !== undefined &&
|
||||
(currentSize !== total || !sizeOptionValues.includes(0)) &&
|
||||
!sizeOptionValues.includes(currentSize)
|
||||
) {
|
||||
options = [...sizeOptions];
|
||||
options.splice(
|
||||
sizeOptionValues.findIndex(x => x > currentSize),
|
||||
0,
|
||||
formatSelectOptions([currentSize])[0],
|
||||
);
|
||||
}
|
||||
const current = currentSize === undefined ? sizeOptionValues[0] : currentSize;
|
||||
const SelectRenderer = selectRenderer || DefaultSelectRenderer;
|
||||
return (
|
||||
<SelectRenderer current={current} options={options} onChange={onChange} />
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user