Compare commits

..
Author SHA1 Message Date
ohif-bot d8055e458f chore(release): publish [skip ci]
- @ohif/extension-cornerstone@1.5.0
 - @ohif/extension-vtk@0.53.5
 - @ohif/core@1.9.0
 - @ohif/ui@0.62.0
 - @ohif/viewer@2.8.0
2019-11-13 20:12:25 +00:00
Igor Octaviano 5c04e34c8f feat: expose UiNotifications service (#1172)
* playing around

* Change extension init and preRegistration signature

* Clear test code

* Update core index test

* Fix test

* Tricking the bug

* Renaming file back to trick some weird bug
2019-11-13 15:02:14 -05:00
Egor Lezhnin c59c5b3f14 feat: filter field for google api windows (#1170) 2019-11-13 09:38:49 -05:00
32 changed files with 328 additions and 78 deletions

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@ export default {
* LIFECYCLE HOOKS
*/
preRegistration(extensionConfiguration) {},
preRegistration({ serviceManager, configuration: extensionConfiguration }) {},
/**
* MODULE GETTERS
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.4.1...@ohif/extension-cornerstone@1.5.0) (2019-11-13)
### Features
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
## [1.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.4.0...@ohif/extension-cornerstone@1.4.1) (2019-11-08)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "1.4.1",
"version": "1.5.0",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
+2 -2
View File
@@ -24,8 +24,8 @@ export default {
* @param {object} [configuration={}]
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
*/
preRegistration(configuration = {}) {
init(configuration);
preRegistration({ serviceManager, configuration = {} }) {
init({ serviceManager, configuration });
},
getViewportModule() {
return OHIFCornerstoneViewport;
+9 -7
View File
@@ -4,16 +4,18 @@ import csTools from 'cornerstone-tools';
import initCornerstoneTools from './initCornerstoneTools.js';
import queryString from 'query-string';
function fallbackMetaDataProvider (type, imageId) {
function fallbackMetaDataProvider(type, imageId) {
if (!imageId.includes('wado?requestType=WADO')) {
return
return;
}
// If you call for an WADO-URI imageId and get no
// If you call for an WADO-URI imageId and get no
// metadata, try reformatting to WADO-RS imageId
const qs = queryString.parse(imageId);
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${qs.seriesUID}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot;
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${
qs.seriesUID
}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
return cornerstone.metaData.get(type, wadoRsImageId);
}
@@ -21,13 +23,12 @@ function fallbackMetaDataProvider (type, imageId) {
// Add this fallback provider with a low priority so it is handled last
cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
/**
*
* @param {object} configuration
* @param {Object|Array} configuration.csToolsConfig
*/
export default function init(configuration = {}) {
export default function init({ serviceManager, configuration = {} }) {
const { csToolsConfig } = configuration;
const { StackManager } = OHIF.utils;
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
@@ -97,6 +98,7 @@ export default function init(configuration = {}) {
];
tools.forEach(tool => csTools.addTool(tool));
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.53.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.4...@ohif/extension-vtk@0.53.5) (2019-11-13)
**Note:** Version bump only for package @ohif/extension-vtk
## [0.53.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.3...@ohif/extension-vtk@0.53.4) (2019-11-12)
**Note:** Version bump only for package @ohif/extension-vtk
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "0.53.4",
"version": "0.53.5",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -52,8 +52,8 @@
"react-vtkjs-viewport": "^0.3.9"
},
"devDependencies": {
"@ohif/core": "^1.8.0",
"@ohif/ui": "^0.61.0",
"@ohif/core": "^1.9.0",
"@ohif/ui": "^0.62.0",
"cornerstone-tools": "^4.0.9",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.6.1",
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.9.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.8.0...@ohif/core@1.9.0) (2019-11-13)
### Features
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
# [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.7.1...@ohif/core@1.8.0) (2019-11-12)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "1.8.0",
"version": "1.9.0",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -2,12 +2,13 @@ import MODULE_TYPES from './MODULE_TYPES.js';
import log from './../log.js';
export default class ExtensionManager {
constructor({ commandsManager }) {
constructor({ commandsManager, servicesManager }) {
this.modules = {};
this.registeredExtensionIds = [];
this.moduleTypeNames = Object.values(MODULE_TYPES);
//
this._commandsManager = commandsManager;
this._servicesManager = servicesManager;
this.moduleTypeNames.forEach(moduleType => {
this.modules[moduleType] = [];
@@ -66,7 +67,10 @@ export default class ExtensionManager {
// preRegistrationHook
if (extension.preRegistration) {
extension.preRegistration(configuration);
extension.preRegistration({
serviceManager: this._servicesManager,
configuration,
});
}
// Register Modules
+9
View File
@@ -1,6 +1,7 @@
import './lib';
import { ExtensionManager, MODULE_TYPES } from './extensions';
import { ServicesManager } from './services';
import classes, { CommandsManager, HotkeysManager } from './classes/';
import DICOMWeb from './DICOMWeb';
@@ -18,12 +19,15 @@ import ui from './ui';
import user from './user.js';
import utils from './utils/';
import { createUiNotificationService } from './services';
const OHIF = {
MODULE_TYPES,
//
CommandsManager,
ExtensionManager,
HotkeysManager,
ServicesManager,
//
utils,
studies,
@@ -41,6 +45,8 @@ const OHIF = {
viewer: {},
measurements,
hangingProtocols,
//
createUiNotificationService,
};
export {
@@ -49,6 +55,7 @@ export {
CommandsManager,
ExtensionManager,
HotkeysManager,
ServicesManager,
//
utils,
studies,
@@ -65,6 +72,8 @@ export {
DICOMWeb,
measurements,
hangingProtocols,
//
createUiNotificationService,
};
export { OHIF };
+3
View File
@@ -8,6 +8,9 @@ describe('Top level exports', () => {
'CommandsManager',
'ExtensionManager',
'HotkeysManager',
'ServicesManager',
//
'createUiNotificationService',
//
'utils',
'studies',
@@ -0,0 +1,9 @@
export default class ServicesManager {
constructor() {
this.services = {};
}
register(service) {
this.services[service.name] = service;
}
}
@@ -0,0 +1,84 @@
/**
* A UI Notification
*
* @typedef {Object} Notification
* @property {string} title -
* @property {string} message -
* @property {number} [duration=5000] - in ms
* @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
* @property {string} [type="info"] - "info" | "error" | "warning" | "success"
* @property {boolean} [autoClose=true]
*/
const uiNotificationServicePublicApi = {
name: 'UINotificationService',
hide,
show,
setServiceImplementation,
};
const uiNotificationServiceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: () => console.warn('show() NOT IMPLEMENTED'),
};
function createUiNotificationService() {
return uiNotificationServicePublicApi;
}
/**
* Create and show a new UI notification; returns the
* ID of the created notification.
*
* @param {Notification} notification { title, message, duration, position, type, autoClose}
* @returns {number} id
*/
function show({
title,
message,
duration = 5000,
position = 'bottomRight',
type = 'info',
autoClose = true,
}) {
return uiNotificationServiceImplementation._show({
title,
message,
duration,
position,
type,
autoClose,
});
}
/**
* Hides/dismisses the notification, if currently shown
*
* @param {number} id - id of the notification to hide/dismiss
* @returns undefined
*/
function hide(id) {
return uiNotificationServiceImplementation._hide({ id });
}
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* }
*/
function setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
}) {
if (hideImplementation) {
uiNotificationServiceImplementation._hide = hideImplementation;
}
if (showImplementation) {
uiNotificationServiceImplementation._show = showImplementation;
}
}
export default createUiNotificationService;
+4
View File
@@ -0,0 +1,4 @@
import ServicesManager from './ServicesManager.js';
import createUiNotificationService from './UINotificationService';
export { createUiNotificationService, ServicesManager };
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.62.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.61.0...@ohif/ui@0.62.0) (2019-11-13)
### Features
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
# [0.61.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.60.1...@ohif/ui@0.61.0) (2019-11-12)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/ui",
"version": "0.61.0",
"version": "0.62.0",
"description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors",
"license": "MIT",
+16 -6
View File
@@ -1,4 +1,10 @@
import React, { useState, createContext, useContext } from 'react';
import React, {
useState,
createContext,
useContext,
useCallback,
useEffect,
} from 'react';
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
@@ -6,7 +12,7 @@ const SnackbarContext = createContext(null);
export const useSnackbarContext = () => useContext(SnackbarContext);
const SnackbarProvider = ({ children }) => {
const SnackbarProvider = ({ children, service }) => {
const DEFAULT_OPTIONS = {
title: '',
message: '',
@@ -19,7 +25,11 @@ const SnackbarProvider = ({ children }) => {
const [count, setCount] = useState(1);
const [snackbarItems, setSnackbarItems] = useState([]);
const show = options => {
useEffect(() => {
service.setServiceImplementation({ hide, show });
}, [service, hide, show]);
const show = useCallback(options => {
if (!options || (!options.title && !options.message)) {
console.warn(
'Snackbar cannot be rendered without required parameters: title | message'
@@ -37,9 +47,9 @@ const SnackbarProvider = ({ children }) => {
setSnackbarItems(state => [...state, newItem]);
setCount(count + 1);
};
});
const hide = id => {
const hide = useCallback(id => {
const hideItem = items => {
const newItems = items.map(item => {
if (item.id === id) {
@@ -57,7 +67,7 @@ const SnackbarProvider = ({ children }) => {
setTimeout(() => {
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
}, 1000);
};
});
const hideAll = () => {
// reset count
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.7.1...@ohif/viewer@2.8.0) (2019-11-13)
### Features
* expose UiNotifications service ([#1172](https://github.com/OHIF/Viewers/issues/1172)) ([5c04e34](https://github.com/OHIF/Viewers/commit/5c04e34c8fb2394ab7acd9eb4f2ab12afeb2f255))
* filter field for google api windows ([#1170](https://github.com/OHIF/Viewers/issues/1170)) ([c59c5b3](https://github.com/OHIF/Viewers/commit/c59c5b3f14d44f1c06aa396125a1f4caaa431c25))
## [2.7.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.7.0...@ohif/viewer@2.7.1) (2019-11-12)
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "2.7.1",
"version": "2.8.0",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -45,14 +45,14 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^1.8.0",
"@ohif/core": "^1.9.0",
"@ohif/extension-cornerstone": "^2.0.0",
"@ohif/extension-dicom-html": "^1.0.1",
"@ohif/extension-dicom-microscopy": "^0.50.6",
"@ohif/extension-dicom-pdf": "^1.0.0",
"@ohif/extension-vtk": "^0.53.4",
"@ohif/extension-vtk": "^0.53.5",
"@ohif/i18n": "^0.52.0",
"@ohif/ui": "^0.61.0",
"@ohif/ui": "^0.62.0",
"@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6",
"core-js": "^3.2.1",
+19 -4
View File
@@ -6,7 +6,9 @@ import './config';
import {
CommandsManager,
ExtensionManager,
ServicesManager,
HotkeysManager,
createUiNotificationService,
utils,
} from '@ohif/core';
import React, { Component } from 'react';
@@ -41,9 +43,16 @@ const commandsManagerConfig = {
getActiveContexts: () => getActiveContexts(store.getState()),
};
// Services
const UINotificationService = createUiNotificationService();
const commandsManager = new CommandsManager(commandsManagerConfig);
const hotkeysManager = new HotkeysManager(commandsManager);
const extensionManager = new ExtensionManager({ commandsManager });
const servicesManager = new ServicesManager();
const extensionManager = new ExtensionManager({
commandsManager,
servicesManager,
});
// ~~~~ END APP SETUP
// TODO[react] Use a provider when the whole tree is React
@@ -76,9 +85,11 @@ class App extends Component {
super(props);
this._appConfig = props;
const { servers, extensions, hotkeys, oidc } = props;
this.initUserManager(oidc);
_initServices([UINotificationService]);
_initExtensions(extensions, hotkeys);
_initServers(servers);
initWebWorkers();
@@ -100,7 +111,7 @@ class App extends Component {
<UserManagerContext.Provider value={userManager}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<SnackbarProvider>
<SnackbarProvider service={UINotificationService}>
<ModalProvider modal={OHIFModal}>
<OHIFStandaloneViewer userManager={userManager} />
</ModalProvider>
@@ -121,7 +132,7 @@ class App extends Component {
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<SnackbarProvider>
<SnackbarProvider service={UINotificationService}>
<ModalProvider modal={OHIFModal}>
<OHIFStandaloneViewer />
</ModalProvider>
@@ -168,6 +179,10 @@ class App extends Component {
}
}
function _initServices(services) {
services.forEach(service => servicesManager.register(service));
}
/**
* @param
*/
@@ -214,4 +229,4 @@ function _makeAbsoluteIfNecessary(url, base_url) {
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
export default ExportedApp;
export { commandsManager, extensionManager, hotkeysManager };
export { commandsManager, extensionManager, hotkeysManager, servicesManager };
@@ -7,8 +7,8 @@ export default {
*/
id: 'measurements-table',
preRegistration(configuration = {}) {
init(configuration);
preRegistration({ serviceManager, configuration = {} }) {
init({ serviceManager, configuration });
},
getPanelModule() {
return {
@@ -34,7 +34,7 @@ const MEASUREMENT_ACTION_MAP = {
* @export
* @param {*} configuration
*/
export default function init(configuration) {
export default function init({ serviceManager, configuration = {} }) {
// If these tools were already added by a different extension, we want to replace
// them with the same tools that have an alternative configuration. By passing in
// our custom `getMeasurementLocationCallback`, we can...
@@ -9,6 +9,7 @@ export default class DatasetPicker extends Component {
error: null,
loading: true,
datasets: [],
filterStr: ""
};
static propTypes = {
@@ -41,15 +42,23 @@ export default class DatasetPicker extends Component {
}
render() {
const { datasets, loading, error } = this.state;
const { datasets, loading, error, filterStr } = this.state;
const { onSelect } = this.props;
return (
<div>
<input class="form-control gcp-input"
type="text"
value={ filterStr }
onChange={ e => this.setState({ filterStr: e.target.value }) }
/>
<DatasetsList
datasets={datasets}
loading={loading}
error={error}
filter={filterStr}
onSelect={onSelect}
/>
</div>
);
}
}
@@ -46,21 +46,23 @@ class DatasetsList extends Component {
}
render() {
if (this.props.error) {
return <p>{this.props.error}</p>;
const { loading, datasets, filter, error } = this.props;
if (error) {
return <p>{error}</p>;
}
const loadingIcon = (
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
);
if (this.props.loading) {
if (loading) {
return loadingIcon;
}
const body = (
<tbody id="DatasetList">
{this.props.datasets.map(this.renderTableRow)}
{datasets.filter(dataset => (dataset.name.split('/')[5].toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
</tbody>
);
@@ -71,7 +73,7 @@ class DatasetsList extends Component {
<th>{this.props.t('Dataset')}</th>
</tr>
</thead>
{this.props.datasets && body}
{datasets && body}
</table>
);
}
@@ -46,20 +46,24 @@ class DicomStoreList extends Component {
}
render() {
if (this.props.error) {
return <p>{this.props.error}</p>;
const { loading, stores, filter, error } = this.props;
if (error) {
return <p>{error}</p>;
}
const loadingIcon = (
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
);
if (this.props.loading) {
if (loading) {
return loadingIcon;
}
const body = (
<tbody id="StoreList">{this.props.stores.map(this.renderTableRow)}</tbody>
<tbody id="StoreList">{
stores.filter(store => (store.name.split('/')[7].toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
</tbody>
);
return (
@@ -69,7 +73,7 @@ class DicomStoreList extends Component {
<th>{this.props.t('DICOM Store')}</th>
</tr>
</thead>
{this.props.stores && body}
{stores && body}
</table>
);
}
@@ -10,6 +10,7 @@ export default class DicomStorePicker extends Component {
loading: true,
stores: [],
locations: [],
filterStr: ""
};
static propTypes = {
@@ -38,16 +39,24 @@ export default class DicomStorePicker extends Component {
}
render() {
const { stores, loading, error } = this.state;
const { stores, loading, error, filterStr } = this.state;
const { onSelect } = this.props;
return (
<DicomStoreList
stores={stores}
loading={loading}
error={error}
onSelect={onSelect}
/>
<div>
<input class="form-control gcp-input"
type="text"
value={ filterStr }
onChange={ e => this.setState({ filterStr: e.target.value }) }
/>
<DicomStoreList
stores={stores}
loading={loading}
error={error}
filter={filterStr}
onSelect={onSelect}
/>
</div>
);
}
}
@@ -9,6 +9,7 @@ export default class LocationPicker extends Component {
error: null,
loading: true,
locations: [],
filterStr: "",
};
static propTypes = {
@@ -37,15 +38,23 @@ export default class LocationPicker extends Component {
}
render() {
const { locations, loading, error } = this.state;
const { locations, loading, error, filterStr } = this.state;
const { onSelect } = this.props;
return (
<LocationsList
locations={locations}
loading={loading}
error={error}
onSelect={onSelect}
/>
<div>
<input class="form-control gcp-input"
type="text"
value={ filterStr }
onChange={ e => this.setState({ filterStr: e.target.value }) }
/>
<LocationsList
locations={locations}
loading={loading}
error={error}
filter={filterStr}
onSelect={onSelect}
/>
</div>
);
}
}
@@ -46,21 +46,23 @@ class LocationsList extends Component {
}
render() {
if (this.props.error) {
return <p>{this.props.error}</p>;
const { loading, locations, filter, error } = this.props;
if (error) {
return <p>{error}</p>;
}
const loadingIcon = (
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
);
if (this.props.loading) {
if (loading) {
return loadingIcon;
}
const body = (
<tbody id="LocationList">
{this.props.locations.map(this.renderTableRow)}
{locations.filter(location => (location.name.split('/')[3].toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
</tbody>
);
@@ -71,7 +73,7 @@ class LocationsList extends Component {
<th>{this.props.t('Location')}</th>
</tr>
</thead>
{this.props.locations && body}
{locations && body}
</table>
);
}
@@ -30,20 +30,29 @@ export default class ProjectPicker extends Component {
this.setState({
projects: response.data.projects || [],
filterStr: "",
loading: false,
});
}
render() {
const { projects, loading, error } = this.state;
const { projects, loading, filterStr, error } = this.state;
const { onSelect } = this.props;
return (
<ProjectsList
projects={projects}
loading={loading}
error={error}
onSelect={onSelect}
/>
<div>
<input class="form-control gcp-input"
type="text"
value={ filterStr }
onChange={ e => this.setState({ filterStr: e.target.value }) }
/>
<ProjectsList
projects={projects}
loading={loading}
filter={filterStr}
error={error}
onSelect={onSelect}
/>
</div>
);
}
}
@@ -50,21 +50,22 @@ class ProjectsList extends Component {
}
render() {
if (this.props.error) {
return <p>{this.props.error}</p>;
const { loading, projects, filter, error } = this.props;
if (error) {
return <p>{error}</p>;
}
const loadingIcon = (
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
);
if (this.props.loading) {
if (loading) {
return loadingIcon;
}
const body = (
<tbody id="ProjectList">
{this.props.projects.map(this.renderTableRow)}
{projects.filter(project => (project.name.toLowerCase().includes(filter.toLowerCase()) || filter=="")).map(this.renderTableRow)}
</tbody>
);
@@ -76,7 +77,7 @@ class ProjectsList extends Component {
<th>{this.props.t('ID')}</th>
</tr>
</thead>
{this.props.projects && body}
{projects && body}
</table>
);
}
@@ -141,3 +141,15 @@
margin: 20px auto;
}
.gcp-input{
height: 40px;
margin: 0 5px 20px 5px;
padding: 0 20px;
cursor: pointer;
border: none;
background-color: var(--input-background-color);
color: var(--input-placeholder-color);
font-size: 10pt;
font-weight: normal;
border-radius: 4px;
}