diff --git a/src/api/clearlyDefined.js b/src/api/clearlyDefined.js index 4e355323d..8da2f9e11 100644 --- a/src/api/clearlyDefined.js +++ b/src/api/clearlyDefined.js @@ -23,6 +23,7 @@ export const ORIGINS_PYPI = 'origins/pypi' export const ORIGINS_RUBYGEMS = 'origins/rubygems' export const ORIGINS_DEBIAN = 'origins/deb' export const ORIGINS_COMPOSER = 'origins/composer' +export const ORIGINS_POD = 'origins/pod' export const ORIGINS = { github: { git: ORIGINS_GITHUB }, npmjs: { npm: ORIGINS_NPM }, @@ -32,7 +33,8 @@ export const ORIGINS = { pypi: { pypi: ORIGINS_PYPI }, rubygems: { gem: ORIGINS_RUBYGEMS }, debian: { deb: ORIGINS_DEBIAN, debsrc: ORIGINS_DEBIAN }, - packagist: { composer: ORIGINS_COMPOSER } + packagist: { composer: ORIGINS_COMPOSER }, + cocoapods: { pod: ORIGINS_POD } } export function getHarvestResults(token, entity) { @@ -218,6 +220,14 @@ export function getComposerRevisions(token, path) { return get(url(`${ORIGINS_COMPOSER}/${path}/revisions`), token) } +export function getCocoaPodsSearch(token, path) { + return get(url(`${ORIGINS_POD}/${path}`), token) +} + +export function getCocoaPodsRevisions(token, path) { + return get(url(`${ORIGINS_POD}/${path}/revisions`), token) +} + export function getRevisions(token, path, type, provider) { const origin = _.get(ORIGINS, `${provider}.${type}`) return get(url(`${origin}/${path}/revisions`), token) diff --git a/src/components/CocoaPodsSelector.js b/src/components/CocoaPodsSelector.js new file mode 100644 index 000000000..ca1bb6f31 --- /dev/null +++ b/src/components/CocoaPodsSelector.js @@ -0,0 +1,66 @@ +// (c) Copyright 2022, SAP SE and ClearlyDefined contributors. Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { getCocoaPodsSearch } from '../api/clearlyDefined' +import { AsyncTypeahead } from 'react-bootstrap-typeahead' +import searchSvg from '../images/icons/searchSvg.svg' +import 'react-bootstrap-typeahead/css/Typeahead.css' + +export default class CocoaPodsSelector extends Component { + static propTypes = { + onChange: PropTypes.func + } + + constructor(props) { + super(props) + this.state = { isLoading: false, options: [], focus: true } + this.getOptions = this.getOptions.bind(this) + this.onChange = this.onChange.bind(this) + } + + onChange(values) { + const { onChange } = this.props + const value = values.length === 0 ? null : values[0] + value && onChange && onChange({ type: 'pod', provider: 'cocoapods', name: value.id }, 'package') + } + + async getOptions(value) { + try { + this.setState({ ...this.state, isLoading: true }) + const options = await getCocoaPodsSearch(this.props.token, value) + this.setState({ ...this.state, options, isLoading: false }) + } catch (error) { + this.setState({ ...this.state, options: [], isLoading: false }) + } + } + + render() { + const { options, isLoading, focus } = this.state + return ( +
+
+ search +
+ this.setState({ ...this.state, focus: true })} + onBlur={() => this.setState({ ...this.state, focus: false })} + clearButton + highlightOnlyResult + emptyLabel="" + selectHintOnEnter + isLoading={isLoading} + onSearch={this.getOptions} + /> +
+ ) + } +} diff --git a/src/components/CocoaPodsVersionPicker.js b/src/components/CocoaPodsVersionPicker.js new file mode 100644 index 000000000..4b0836df5 --- /dev/null +++ b/src/components/CocoaPodsVersionPicker.js @@ -0,0 +1,91 @@ +// (c) Copyright 2022, SAP SE and ClearlyDefined contributors. Licensed under the MIT license. +// SPDX-License-Identifier: MIT + +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { getCocoaPodsRevisions } from '../api/clearlyDefined' +import Autocomplete from './Navigation/Ui/Autocomplete' +import searchSvg from '../images/icons/searchSvg.svg' + +export default class CocoaPodsVersionPicker extends Component { + static propTypes = { + onChange: PropTypes.func, + request: PropTypes.object.isRequired, + defaultInputValue: PropTypes.string + } + + constructor(props) { + super(props) + this.state = { + customValues: [], + options: [], + focus: false + } + this.onChange = this.onChange.bind(this) + this.filter = this.filter.bind(this) + } + + componentDidMount() { + this.getOptions('') + } + + async getOptions(value) { + try { + const { name } = this.props.request + const options = await getCocoaPodsRevisions(this.props.token, name) + this.setState({ ...this.state, options }) + } catch (error) { + this.setState({ ...this.state, options: [] }) + } + } + + onChange(values) { + const { onChange } = this.props + if (!onChange) return + let value = values.length === 0 ? null : values[0] + if (!value) return onChange(value) + if (value.customOption) { + value = value.label + this.setState({ ...this.state, customValues: [...this.state.customValues, value] }) + } + onChange(value) + } + + filter(option, props) { + if (this.props.request.revision) return true + return option.toLowerCase().indexOf(props.text.toLowerCase()) !== -1 + } + + render() { + const { defaultInputValue, request } = this.props + const selected = request.revision ? [request.revision] : [] + const { customValues, options, focus } = this.state + const list = customValues.concat(options) + return ( +
+
+ search +
{' '} + this.setState({ ...this.state, focus: true })} + onBlur={() => this.setState({ ...this.state, focus: false })} + positionFixed + clearButton + allowNew + newSelectionPrefix="Version:" + emptyLabel="" + filterBy={this.filter} + selectHintOnEnter + /> +
+ ) + } +} diff --git a/src/components/DefinitionEntry.js b/src/components/DefinitionEntry.js index 2d5907e77..bc24e603a 100644 --- a/src/components/DefinitionEntry.js +++ b/src/components/DefinitionEntry.js @@ -16,6 +16,7 @@ import nuget from '../images/nuget.svg' import debian from '../images/debian.png' import maven from '../images/maven.png' import composer from '../images/packagist.png' +import pod from '../images/pod.png' import Contribution from '../utils/contribution' import Definition from '../utils/definition' import Curation from '../utils/curation' @@ -425,6 +426,7 @@ class DefinitionEntry extends React.Component { @@ -455,6 +457,7 @@ class DefinitionEntry extends React.Component { if (definition.coordinates.type === 'nuget') return nuget if (definition.coordinates.type === 'deb') return debian if (definition.coordinates.type === 'composer') return composer + if (definition.coordinates.type === 'pod') return pod return null } diff --git a/src/components/FileList/FileList.js b/src/components/FileList/FileList.js index f36772613..ca70dcd69 100644 --- a/src/components/FileList/FileList.js +++ b/src/components/FileList/FileList.js @@ -11,6 +11,8 @@ import FacetsDropdown from '../../components/FacetsDropdown' import Contribution from '../../utils/contribution' import FileListSpec from '../../utils/filelist' import Attachments from '../../utils/attachments' +import ModalEditor from '../ModalEditor' +import EnhancedLicensePicker from '../../utils/EnhancedLicensePicker' import folderIcon from '../../images/icons/folder.svg' import fileIcon from '../../images/icons/file.svg' import FolderOpenIcon from '@material-ui/icons/FolderOpen' @@ -235,6 +237,16 @@ export default class FileList extends PureComponent { onChange(`described.facets.${facet}`, newGlobs) } + onLicenseChange = (record, license) => { + const { onChange, component, previewDefinition } = this.props + const attributions = Contribution.getValue(component.item, previewDefinition, `files[${record.id}].attributions`) + onChange(`files[${record.id}]`, license, null, license => ({ + path: record.path, + license, + ...(attributions ? { attributions } : {}) + })) + } + onDirectorySelect = e => { let tempdata = this.state.breadcrumbs tempdata.push(e) @@ -281,6 +293,25 @@ export default class FileList extends PureComponent { ) } + renderLicenseCell = (value, record) => { + const { readOnly, component, previewDefinition } = this.props + const field = `files[${record.id}].license` + const editor = EnhancedLicensePicker + return ( + !record.children && ( + this.onLicenseChange(record, license)} /> + ) + ) + } + render() { const { definition, component, previewDefinition } = this.props const { expandedRows, searchText, filteredFiles, files } = this.state @@ -359,14 +390,8 @@ export default class FileList extends PureComponent { // }} // /> // ), - render: (value, record) => { - let license = Contribution.getValue(component.item, previewDefinition, `files[${record.id}].license`) - return ( - !record.children && - (license ?

{license}

:

SPDX license

) - ) - }, - width: '15%' + render: this.renderLicenseCell, + width: '20%' }, { title: 'Copyrights', @@ -376,31 +401,6 @@ export default class FileList extends PureComponent { // ...this.getColumnSearchProps('attributions'), render: (value, record) => this.renderCopyrightCell(record, component, previewDefinition), width: '25%' - }, - { - title: '', - dataIndex: 'edit', - key: 'edit', - className: 'edit-data', - // ...this.getColumnSearchProps('attributions'), - render: (value, record) => - !record.children && ( - - - - - ), - width: '5%' } ] diff --git a/src/components/Footer.js b/src/components/Footer.js index 382169d95..87274e02b 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -34,10 +34,11 @@ export default class Footer extends Component {

- ClearlyDefined was created by - Microsoft - in partnership with the - Open Source Initiative. + ClearlyDefined is an + OSI incubator project, + originally contributed with ❤️ by + Microsoft + and maintained by a growing community.

diff --git a/src/components/HarvestQueueList.js b/src/components/HarvestQueueList.js index 539a78539..a2f62ae43 100644 --- a/src/components/HarvestQueueList.js +++ b/src/components/HarvestQueueList.js @@ -15,7 +15,8 @@ import { DebianVersionPicker, NuGetVersionPicker, RubyGemsVersionPicker, - ComposerVersionPicker + ComposerVersionPicker, + CocoaPodsVersionPicker } from './' import { getGitHubRevisions } from '../api/clearlyDefined' import { clone } from 'lodash' @@ -28,6 +29,7 @@ import cargo from '../images/cargo.png' import maven from '../images/maven.png' import nuget from '../images/nuget.png' import composer from '../images/packagist.png' +import pod from '../images/pod.png' class HarvestQueueList extends React.Component { static propTypes = { @@ -39,7 +41,7 @@ class HarvestQueueList extends React.Component { } static defaultProps = { - loadMoreRows: () => {} + loadMoreRows: () => { } } constructor(props) { @@ -106,6 +108,9 @@ class HarvestQueueList extends React.Component { {request.provider === 'packagist' && ( )} + {request.provider === 'cocoapods' && ( + + )}
) @@ -150,6 +155,7 @@ class HarvestQueueList extends React.Component { if (request.provider === 'nuget') return nuget if (request.provider === 'debian') return debian if (request.provider === 'packagist') return composer + if (request.provider === 'cocoapods') return pod return null } @@ -161,7 +167,7 @@ class HarvestQueueList extends React.Component { renderRow({ index, key, style }) { const { list } = this.props const request = list[index] - const clickHandler = () => {} + const clickHandler = () => { } return (
} {activeProvider.value === 'rubygems' && } {activeProvider.value === 'debian' && } + {activeProvider.value === 'cocoapods' && } {this.renderActionButton()} diff --git a/src/components/Navigation/Sections/DescribedSection.js b/src/components/Navigation/Sections/DescribedSection.js index 2dcf7caf6..0638bf95d 100644 --- a/src/components/Navigation/Sections/DescribedSection.js +++ b/src/components/Navigation/Sections/DescribedSection.js @@ -6,7 +6,6 @@ import get from 'lodash/get' import Contribution from '../../../utils/contribution' import TwoColumnsSection from '../Sections/TwoColumnsSection' import { SourcePicker } from '../..' -import ListDataRenderer from '../Ui/ListDataRenderer' import FacetsTooltipIcon from '../Ui/FacetsTooltipIcon' import FacetsEditor from '../../FacetsEditor' // import infoIcon from '../../../images/icons/infoIcon.svg' @@ -82,7 +81,7 @@ class DescribedSection extends Component { label: 'Tools', customBox: true, customBoxIcon: tools, - component: + component: {toolListView} }, { label: 'Curations', diff --git a/src/components/Navigation/Ui/ListDataRenderer.js b/src/components/Navigation/Ui/ListDataRenderer.js index 5f517b950..8498ef8bd 100644 --- a/src/components/Navigation/Ui/ListDataRenderer.js +++ b/src/components/Navigation/Ui/ListDataRenderer.js @@ -56,12 +56,13 @@ export default class ListDataRenderer extends Component { className="popoverSpan" onMouseOver={() => this.dispatchShowTooltip()} > - {data} + {data.join(', ')} this.setState({ showTooltip: false })} diff --git a/src/components/PageAbout.js b/src/components/PageAbout.js index e147e4669..89a054c5f 100644 --- a/src/components/PageAbout.js +++ b/src/components/PageAbout.js @@ -116,7 +116,7 @@ class PageAbout extends Component {

Centralized

- ClearlyDefined is you central hub for all things Open Source Software licenses. It’s here you’ll + ClearlyDefined is your central hub for all things Open Source Software licenses. It’s here you’ll find, or contribute, to critical license data.

diff --git a/src/components/index.js b/src/components/index.js index aab3984c6..7f192105c 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -71,6 +71,10 @@ export { default as RubyGemsSelector } from './RubyGemsSelector' export { default as RubyGemsVersionPicker } from './RubyGemsVersionPicker' +export { default as CocoaPodsSelector } +from './CocoaPodsSelector' +export { default as CocoaPodsVersionPicker } +from './CocoaPodsVersionPicker' export { default as PageAbout } from './PageAbout' export { default as RehydrationProvider } diff --git a/src/styles/_FullDetailComponent.scss b/src/styles/_FullDetailComponent.scss index a4ef3cda0..587d7b8bc 100644 --- a/src/styles/_FullDetailComponent.scss +++ b/src/styles/_FullDetailComponent.scss @@ -291,6 +291,10 @@ } } +.column-license .edit-icon { + font-size: 0; +} + .tile-row__definition i, .tile-row .edit-icon { position: absolute; @@ -754,4 +758,4 @@ padding: 0; } } -} +} \ No newline at end of file diff --git a/src/styles/_Popover.scss b/src/styles/_Popover.scss index b771a57ff..c10df30b2 100644 --- a/src/styles/_Popover.scss +++ b/src/styles/_Popover.scss @@ -51,10 +51,21 @@ text-overflow: ellipsis; width: 100%; display: block; + cursor: pointer; } +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} - +.popover-content .popoverRenderer { + padding: 9px 14px; +} // ---------------------------------------- // Modal @@ -211,4 +222,4 @@ button.modal__btn--secondary, .fade.in.modal { opacity: 1 !important; -} +} \ No newline at end of file diff --git a/src/utils/contribution.js b/src/utils/contribution.js index e02f8d446..e2c796315 100644 --- a/src/utils/contribution.js +++ b/src/utils/contribution.js @@ -20,6 +20,7 @@ import nuget from '../images/nuget.svg' import debian from '../images/debian.png' import composer from '../images/packagist.png' import maven from '../images/maven.png' +import pod from '../images/pod.png' import moment from 'moment' import { difference } from './utils' @@ -225,6 +226,8 @@ export default class Contribution { return debian case 'mavencentral': return maven + case 'cocoapods': + return pod default: return null }