diff --git a/packages/grafana-data/src/vector/FunctionalVector.ts b/packages/grafana-data/src/vector/FunctionalVector.ts index 2e999704da2..6a6c719be2a 100644 --- a/packages/grafana-data/src/vector/FunctionalVector.ts +++ b/packages/grafana-data/src/vector/FunctionalVector.ts @@ -27,7 +27,7 @@ export abstract class FunctionalVector implements Vector, Iterable(predicate: (item: T) => V) { + filter(predicate: (item: T) => boolean): T[] { return vectorator(this).filter(predicate); } @@ -65,7 +65,8 @@ export function vectorator(vector: Vector) { return result; }, - filter(predicate: (item: T) => V) { + /** Add a predicate where you return true if it should *keep* the value */ + filter(predicate: (item: T) => boolean): T[] { const result: T[] = []; for (const val of this) { if (predicate(val)) { diff --git a/public/app/features/dimensions/editors/ResourcePicker.tsx b/public/app/features/dimensions/editors/ResourcePicker.tsx index db4a6b400cd..4992c8f9937 100644 --- a/public/app/features/dimensions/editors/ResourcePicker.tsx +++ b/public/app/features/dimensions/editors/ResourcePicker.tsx @@ -12,11 +12,12 @@ import { stylesFactory, } from '@grafana/ui'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; -import { getBackendSrv } from '@grafana/runtime'; import { ResourceCards } from './ResourceCards'; import SVG from 'react-inlinesvg'; import { css } from '@emotion/css'; import { getPublicOrAbsoluteUrl } from '../resource'; +import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; +import { FileElement, GrafanaDatasource } from 'app/plugins/datasource/grafana/datasource'; interface Props { value?: string; //img/icons/unicons/0-plus.svg @@ -24,6 +25,13 @@ interface Props { mediaType: 'icon' | 'image'; } +interface ResourceItem { + label: string; + value: string; + search: string; + imgUrl: string; +} + export function ResourcePicker(props: Props) { const { value, onChange, mediaType } = props; const folders = (mediaType === 'icon' ? ['img/icons/unicons', 'img/icons/iot'] : ['img/bg']).map((v) => ({ @@ -36,43 +44,52 @@ export function ResourcePicker(props: Props) { { label: 'Select', active: true }, // { label: 'Upload', active: false }, ]); - const [directoryIndex, setDirectoryIndex] = useState([]); - const [defaultList, setDefaultList] = useState([]); + const [directoryIndex, setDirectoryIndex] = useState([]); + const [filteredIndex, setFilteredIndex] = useState([]); const theme = useTheme2(); const styles = getStyles(theme); useEffect(() => { // we don't want to load everything before picking a folder - if (currentFolder) { - getBackendSrv() - .get(`public/${currentFolder?.value}/index.json`) - .then((data) => { - const cards = data.files.map((v: string) => ({ - value: v, - label: v, - imgUrl: `public/${currentFolder?.value}/${v}`, - })); - setDirectoryIndex(cards); - setDefaultList(cards); - }) - .catch((e) => console.error(e)); - } else { - return; + const folder = currentFolder?.value; + if (folder) { + const filter = + mediaType === 'icon' + ? (item: FileElement) => item.name.endsWith('.svg') + : (item: FileElement) => item.name.endsWith('.png') || item.name.endsWith('.gif'); + + getDatasourceSrv() + .get('-- Grafana --') + .then((ds) => { + (ds as GrafanaDatasource).listFiles(folder).subscribe({ + next: (frame) => { + const cards: ResourceItem[] = []; + frame.forEach((item) => { + if (filter(item)) { + const idx = item.name.lastIndexOf('.'); + cards.push({ + value: item.name, + label: item.name, + search: (idx ? item.name.substr(0, idx) : item.name).toLowerCase(), + imgUrl: `public/${folder}/${item.name}`, + }); + } + }); + setDirectoryIndex(cards); + setFilteredIndex(cards); + }, + }); + }); } - }, [currentFolder]); + }, [mediaType, currentFolder]); const onChangeSearch = (e: ChangeEvent) => { - if (e.target.value) { - const filtered = directoryIndex.filter((card) => - card.value - // exclude file type (.svg) in the search - .substr(0, card.value.length - 4) - .toLocaleLowerCase() - .includes(e.target.value.toLocaleLowerCase()) - ); - setDirectoryIndex(filtered); + let query = e.currentTarget.value; + if (query) { + query = query.toLowerCase(); + setFilteredIndex(directoryIndex.filter((card) => card.search.includes(query))); } else { - setDirectoryIndex(defaultList); + setFilteredIndex(directoryIndex); } }; const imgSrc = getPublicOrAbsoluteUrl(value!); @@ -106,9 +123,9 @@ export function ResourcePicker(props: Props) {
- {directoryIndex ? ( + {filteredIndex ? (
- +
) : ( diff --git a/public/app/plugins/datasource/grafana/datasource.ts b/public/app/plugins/datasource/grafana/datasource.ts index 6e343a31819..4df139e5455 100644 --- a/public/app/plugins/datasource/grafana/datasource.ts +++ b/public/app/plugins/datasource/grafana/datasource.ts @@ -3,6 +3,7 @@ import { DataSourceWithBackend, getBackendSrv, getGrafanaLiveSrv, getTemplateSrv import { AnnotationQuery, AnnotationQueryRequest, + DataFrameView, DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings, @@ -17,6 +18,7 @@ import { GrafanaAnnotationQuery, GrafanaAnnotationType, GrafanaQuery, GrafanaQue import AnnotationQueryEditor from './components/AnnotationQueryEditor'; import { getDashboardSrv } from '../../../features/dashboard/services/DashboardSrv'; import { isString } from 'lodash'; +import { map } from 'rxjs/operators'; let counter = 100; @@ -124,6 +126,23 @@ export class GrafanaDatasource extends DataSourceWithBackend { return of(); // nothing } + listFiles(path: string): Observable> { + return this.query({ + targets: [ + { + refId: 'A', + queryType: GrafanaQueryType.List, + path, + }, + ], + } as any).pipe( + map((v) => { + const frame = v.data[0] ?? toDataFrame({}); + return new DataFrameView(frame); + }) + ); + } + metricFindQuery(options: any) { return Promise.resolve([]); } @@ -183,3 +202,8 @@ export class GrafanaDatasource extends DataSourceWithBackend { return Promise.resolve(); } } + +export interface FileElement { + name: string; + ['media-type']: string; +} diff --git a/public/img/bg/index.json b/public/img/bg/index.json deleted file mode 100644 index cb799fec2e9..00000000000 --- a/public/img/bg/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "bg", - "files": ["p0.png", "p1.png", "p2.png", "p3.png", "p4.png", "p5.png", "p6.png"] -} diff --git a/public/img/icons/iot/index.json b/public/img/icons/iot/index.json deleted file mode 100644 index 4edb38c86db..00000000000 --- a/public/img/icons/iot/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "line", - "files": ["faucet.svg", "pump.svg"] -}