Query Select Target Dropdown (#499)

This commit is contained in:
Kyle Knight
2016-11-23 15:10:32 -06:00
committed by GitHub
parent 685e3da5e7
commit 46dee08ab3
16 changed files with 807 additions and 228 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ rules:
no-css-comments: 1
no-duplicate-properties: 1
no-empty-rulesets: 1
no-extends: 1
no-extends: 0
no-ids: 1
no-important: 1
no-invalid-hex: 1
Binary file not shown.
@@ -4,7 +4,7 @@
border-radius: 2px;
background-color: $white;
border: solid 1px $accent-medium;
font-size: $base;
font-size: 16px;
padding: $pad-xsmall $pad-base;
color: $text-dark;
font-family: 'Oxygen', sans-serif;
@@ -1,5 +1,6 @@
import React, { Component, PropTypes } from 'react';
import { isEqual, noop } from 'lodash';
import classnames from 'classnames';
import Kolide from 'kolide';
import targetInterface from 'interfaces/target';
@@ -22,6 +23,7 @@ class SelectTargetsDropdown extends Component {
super(props);
this.state = {
isEmpty: false,
isLoadingTargets: false,
moreInfoTarget: null,
query: '',
@@ -57,8 +59,6 @@ class SelectTargetsDropdown extends Component {
const currentMoreInfoTarget = this.state.moreInfoTarget || {};
if (isEqual(moreInfoTarget.display_text, currentMoreInfoTarget.display_text)) {
this.setState({ moreInfoTarget: null });
return false;
}
@@ -75,13 +75,18 @@ class SelectTargetsDropdown extends Component {
});
}
this.setState({ moreInfoTarget });
return false;
};
}
onBackToResults = () => {
this.setState({
moreInfoTarget: null,
});
}
fetchTargets = (query, selectedTargets = this.props.selectedTargets) => {
const { onFetchTargets } = this.props;
@@ -93,6 +98,14 @@ class SelectTargetsDropdown extends Component {
targets,
} = response;
if (targets.length === 0) {
// We don't want the lib's default "No Results" so we fake it
targets.push({});
this.setState({ isEmpty: true });
} else {
this.setState({ isEmpty: false });
}
onFetchTargets(query, response);
this.setState({ isLoadingTargets: false, targets });
@@ -107,13 +120,19 @@ class SelectTargetsDropdown extends Component {
}
render () {
const { isLoadingTargets, moreInfoTarget, targets } = this.state;
const { fetchTargets, onInputClose, onTargetSelectMoreInfo } = this;
const { isEmpty, isLoadingTargets, moreInfoTarget, targets } = this.state;
const { fetchTargets, onBackToResults, onInputClose, onTargetSelectMoreInfo } = this;
const { onSelect, selectedTargets } = this.props;
const menuRenderer = Menu(onTargetSelectMoreInfo, moreInfoTarget);
const menuRenderer = Menu(onTargetSelectMoreInfo, moreInfoTarget, onBackToResults);
const inputClasses = classnames({
'show-preview': moreInfoTarget,
'is-empty': isEmpty,
});
return (
<Input
className={inputClasses}
isLoading={isLoadingTargets}
menuRenderer={menuRenderer}
onClose={onInputClose}
@@ -7,6 +7,7 @@ import targetInterface from 'interfaces/target';
class SelectTargetsInput extends Component {
static propTypes = {
className: PropTypes.string,
isLoading: PropTypes.bool,
menuRenderer: PropTypes.func,
onClose: PropTypes.func,
@@ -24,6 +25,7 @@ class SelectTargetsInput extends Component {
render () {
const {
className,
isLoading,
menuRenderer,
onClose,
@@ -35,7 +37,7 @@ class SelectTargetsInput extends Component {
return (
<Select
className="target-select"
className={`${className} target-select`}
isLoading={isLoading}
filterOptions={this.filterOptions}
labelKey="display_text"
@@ -48,6 +50,8 @@ class SelectTargetsInput extends Component {
onInputChange={onTargetSelectInputChange}
placeholder="Label Name, Host Name, IP Address, etc."
resetValue={[]}
scrollMenuIntoView={false}
tabSelectsValue={false}
value={selectedTargets}
valueKey="display_text"
/>
@@ -6,8 +6,9 @@ import targetInterface from 'interfaces/target';
import TargetDetails from '../TargetDetails';
import TargetOption from '../TargetOption';
const baseClass = 'target-list';
const SelectTargetsMenuWrapper = (onMoreInfoClick, moreInfoTarget) => {
const SelectTargetsMenuWrapper = (onMoreInfoClick, moreInfoTarget, handleBackToResults) => {
const SelectTargetsMenu = ({
focusedOption,
instancePrefix,
@@ -21,9 +22,14 @@ const SelectTargetsMenuWrapper = (onMoreInfoClick, moreInfoTarget) => {
onOptionRef,
}) => {
const Option = optionComponent;
const renderTargets = (targetType) => {
const targets = filter(options, { target_type: targetType });
if (targets.length === 0) {
return <span className={`${baseClass}__not-found`}>Unable to find any matching {targetType}.</span>;
}
return targets.map((target, index) => {
const { disabled: isDisabled } = target;
const isSelected = includes(valueArray, target);
@@ -61,14 +67,14 @@ const SelectTargetsMenuWrapper = (onMoreInfoClick, moreInfoTarget) => {
};
return (
<div>
<div>
<div>hosts</div>
{renderTargets('hosts')}
<div>labels</div>
<div className={baseClass}>
<div className={`${baseClass}__options`}>
<p className={`${baseClass}__type`}>labels</p>
{renderTargets('labels')}
<p className={`${baseClass}__type`}>hosts</p>
{renderTargets('hosts')}
</div>
<TargetDetails target={moreInfoTarget} />
<TargetDetails target={moreInfoTarget} className={`${baseClass}__spotlight`} handleBackToResults={handleBackToResults} />
</div>
);
};
@@ -0,0 +1,45 @@
.target-list {
&__type {
background-color: $bg-medium;
line-height: $larger;
padding: 0 $base;
margin: 0;
text-transform: uppercase;
font-weight: $light;
font-size: $mini;
color: $text-dark;
letter-spacing: 0.5px;
}
&__options {
width: 57%;
float: left;
overflow-y: auto;
box-shadow: 0 8px 12px 0 rgba(0, 0, 0, 0.15);
min-height: 500px;
position: relative;
z-index: 2;
background-color: $bg-light;
}
&__spotlight {
width: 43%;
float: right;
overflow-y: auto;
background-color: $white;
min-height: 500px;
position: relative;
z-index: 1;
}
&__not-found {
font-size: $small;
font-weight: $bold;
line-height: 2.64;
text-align: left;
color: $text-dark;
text-transform: uppercase;
display: block;
padding: 0 $large;
}
}
@@ -1,10 +1,9 @@
import React, { Component } from 'react';
import React, { Component, PropTypes } from 'react';
import { noop } from 'lodash';
import AceEditor from 'react-ace';
import classnames from 'classnames';
import hostHelpers from 'components/hosts/HostDetails/helpers';
import ShadowBox from 'components/ShadowBox';
import ShadowBoxInput from 'components/forms/fields/ShadowBoxInput';
import targetInterface from 'interfaces/target';
const baseClass = 'target-details';
@@ -12,10 +11,28 @@ const baseClass = 'target-details';
class TargetDetails extends Component {
static propTypes = {
target: targetInterface,
className: PropTypes.string,
handleBackToResults: PropTypes.func,
};
static defaultProps = {
handleBackToResults: noop,
};
onlineHosts = (labelBaseClass, online) => {
if (online > 0) {
return (
<span className={`${labelBaseClass}__hosts-online`}> ({online}% ONLINE)</span>
);
}
return false;
};
renderHost = () => {
const { className, handleBackToResults, target } = this.props;
const {
display_text: displayText,
ip,
mac,
memory,
@@ -23,7 +40,7 @@ class TargetDetails extends Component {
osVersion,
platform,
status,
} = this.props.target;
} = target;
const hostBaseClass = 'host-target';
const isOnline = status === 'online';
const isOffline = status === 'offline';
@@ -34,63 +51,104 @@ class TargetDetails extends Component {
);
return (
<div>
<p className={statusClassName}>{status}</p>
<ShadowBox>
<table className={`${baseClass}__table`}>
<tbody>
<tr>
<th>IP Address</th>
<td>{ip}</td>
</tr>
<tr>
<th>MAC Address</th>
<td>{mac}</td>
</tr>
<tr>
<th>Platform</th>
<td>
<i className={hostHelpers.platformIconClass(platform)} />
<span className={`${hostBaseClass}__platform-text`}>{platform}</span>
</td>
</tr>
<tr>
<th>Operating System</th>
<td>{osVersion}</td>
</tr>
<tr>
<th>Osquery Version</th>
<td>{osqueryVersion}</td>
</tr>
<tr>
<th>Memory</th>
<td>{hostHelpers.humanMemory(memory)}</td>
</tr>
</tbody>
</table>
</ShadowBox>
<div className={`${hostBaseClass} ${className}`}>
<button className={`button button--unstyled ${hostBaseClass}__back`} onClick={handleBackToResults}>
<i className="kolidecon kolidecon-chevronleft" />Back
</button>
<p className={`${hostBaseClass}__display-text`}>
<i className={`${hostBaseClass}__icon kolidecon-fw kolidecon-single-host`} />
<span>{displayText}</span>
</p>
<p className={statusClassName}>
{isOnline && <i className={`${hostBaseClass}__icon ${hostBaseClass}__icon--online kolidecon-fw kolidecon-success-check`} />}
{isOffline && <i className={`${hostBaseClass}__icon ${hostBaseClass}__icon--offline kolidecon-fw kolidecon-offline`} />}
<span>{status}</span>
</p>
<table className={`${baseClass}__table`}>
<tbody>
<tr>
<th>IP Address</th>
<td>{ip}</td>
</tr>
<tr>
<th>MAC Address</th>
<td><span className={`${hostBaseClass}__mac-address`}>{mac}</span></td>
</tr>
<tr>
<th>Platform</th>
<td>
<i className={hostHelpers.platformIconClass(platform)} />
<span className={`${hostBaseClass}__platform-text`}> {platform}</span>
</td>
</tr>
<tr>
<th>Operating System</th>
<td>{osVersion}</td>
</tr>
<tr>
<th>Osquery Version</th>
<td>{osqueryVersion}</td>
</tr>
<tr>
<th>Memory</th>
<td>{hostHelpers.humanMemory(memory)}</td>
</tr>
</tbody>
</table>
<div className={`${hostBaseClass}__labels-wrapper`}>
<div className={`${hostBaseClass}__labels-wrapper--header`}>
<i className="kolidecon-label" />
<p className={`${hostBaseClass}__labels-header`}>
<i className={`${hostBaseClass}__icon kolidecon-fw kolidecon-label`} />
<span>Labels</span>
</div>
</p>
<ul className={`${hostBaseClass}__labels-list`}>
<li>Engineering</li>
<li>DevOps</li>
<li>ElCapDev</li>
<li>Workstation</li>
</ul>
</div>
</div>
);
}
renderLabel = () => {
const { hosts, query } = this.props.target;
const { onlineHosts } = this;
const { handleBackToResults, className, target } = this.props;
const {
count,
description,
display_text: displayText,
online,
query,
} = target;
const labelBaseClass = 'label-target';
return (
<div>
<div className={`${labelBaseClass}__text-editor-wrapper`}>
<div className={`${labelBaseClass} ${className}`}>
<button className={`button button--unstyled ${labelBaseClass}__back`} onClick={handleBackToResults}>
<i className="kolidecon kolidecon-chevronleft" /> Back
</button>
<p className={`${labelBaseClass}__display-text`}>
<i className={`${labelBaseClass}__icon kolidecon-fw kolidecon-label`} />
<span>{displayText}</span>
</p>
<p className={`${labelBaseClass}__hosts`}>
<span className={`${labelBaseClass}__hosts-count`}><strong>{count}</strong>HOSTS</span>
{ onlineHosts(labelBaseClass, online) }
</p>
<p className={`${labelBaseClass}__description`}>{description || 'No Description'}</p>
<div className={`${labelBaseClass}__editor`}>
<AceEditor
editorProps={{ $blockScrolling: Infinity }}
mode="kolide"
minLines={4}
maxLines={4}
fontSize={13}
name="label-query"
readOnly
setOptions={{ wrap: true }}
@@ -101,42 +159,6 @@ class TargetDetails extends Component {
width="100%"
/>
</div>
<div className={`${labelBaseClass}__search-section`}>
<ShadowBoxInput
iconClass="kolidecon-search"
name="search-hosts"
placeholder="SEARCH HOSTS"
/>
<div className={`${labelBaseClass}__num-hosts-section`}>
<span className="num-hosts">{hosts.length} HOSTS</span>
</div>
</div>
<ShadowBox>
<table className={`${baseClass}__table`}>
<thead>
<tr>
<th>Hostname</th>
<th>Status</th>
<th>Platform</th>
<th>Location</th>
<th>MAC</th>
</tr>
</thead>
<tbody>
{hosts.map((host) => {
return (
<tr className="__label-row" key={`host-${host.id}`}>
<td>{host.hostname}</td>
<td>{host.status}</td>
<td><i className={hostHelpers.platformIconClass(host.platform)} /></td>
<td>{host.ip}</td>
<td>{host.mac}</td>
</tr>
);
})}
</tbody>
</table>
</ShadowBox>
</div>
);
}
@@ -0,0 +1,181 @@
.host-target,
.label-target {
padding: $pad-large;
&__back {
color: $text-dark;
font-size: $xsmall;
display: none;
@include breakpoint(tablet) {
display: inline-block;
position: absolute;
top: 10px;
left: 10px;
padding: 10px;
&:active {
top: 10px;
}
}
}
@include breakpoint(tablet) {
padding-top: $pad-most;
}
}
.host-target {
&__display-text {
font-size: $medium;
font-weight: $bold;
letter-spacing: 0.6px;
color: $text-ultradark;
margin: 0;
}
&__icon {
color: $accent-text;
margin-right: $pad-small;
width: 21px;
&--online {
color: $success;
}
&--offline {
color: $alert;
}
}
&__status {
margin: $pad-xsmall 0;
font-size: $small;
font-weight: $bold;
letter-spacing: 1.3px;
text-transform: uppercase;
&--is-online {
color: $success;
}
&--is-offline {
color: $alert;
}
}
&__labels-wrapper {
position: absolute;
bottom: $pad-large;
}
&__labels-header {
font-size: $small;
font-weight: $bold;
letter-spacing: 0.9px;
color: $text-dark;
text-transform: uppercase;
margin: 0;
}
&__labels-list {
font-size: $small;
font-weight: $normal;
line-height: 1.85;
letter-spacing: 0.5px;
color: $text-medium;
list-style: none;
margin: 0;
padding: 0;
li {
display: inline-block;
&::after {
content: ',';
margin-right: 3px;
}
&:last-child {
&::after {
content: '';
margin: 0;
}
}
}
}
&__platform-text {
text-transform: capitalize;
}
&__mac-address {
text-transform: uppercase;
}
}
.label-target {
&__display-text {
font-size: $medium;
font-weight: $bold;
letter-spacing: 0.6px;
color: $text-ultradark;
margin: 0;
}
&__icon {
color: $accent-text;
margin-right: $pad-small;
width: 21px;
}
&__hosts {
margin: $pad-xsmall 0;
letter-spacing: 0.4px;
font-size: $small;
font-weight: $normal;
}
&__hosts-count {
font-weight: $bold;
color: $text-dark;
strong {
min-width: 21px;
text-align: center;
font-weight: $bold;
display: inline-block;
margin-right: $pad-small;
}
}
&__hosts-online {
color: $success;
}
&__description {
font-size: $pad-medium;
font-weight: $normal;
line-height: 1.71;
color: $text-dark;
margin: 0 0 $pad-small;
}
}
.target-details {
&__table {
border-collapse: collapse;
border-spacing: 0;
text-align: left;
font-size: $small;
width: 100%;
margin-bottom: $pad-medium;
th {
font-weight: $bold;
line-height: 2.3;
color: $text-dark;
padding-right: $pad-half;
}
}
}
@@ -29,7 +29,12 @@ class TargetOption extends Component {
const { count, ip, target_type: targetType } = target;
if (targetType === 'hosts') {
return <span className={`${baseClass}__ip`}>{ip}</span>;
return (
<span>
<span className={`${baseClass}__delimeter`}>&bull;</span>
<span className={`${baseClass}__ip`}>{ip}</span>
</span>
);
}
return <span className={`${baseClass}__count`}>{count} hosts</span>;
@@ -57,7 +62,6 @@ class TargetOption extends Component {
{targetType === 'hosts' && <i className={`${baseClass}__icon ${hostPlatformIconClass()}`} />}
{targetType === 'labels' && <i className={`${baseClass}__icon kolidecon-label`} />}
<span className={`${baseClass}__label-label`}>{displayText}</span>
<span className={`${baseClass}__delimeter`}>&bull;</span>
{renderTargetDetail()}
</button>
</div>
@@ -7,13 +7,41 @@
font-size: 15px;
}
&__add-btn {
color: $accent-medium;
font-size: $medium;
padding: 0 $pad-half;
}
&__wrapper {
@include display(flex);
@include align-items(stretch);
@include align-content(stretch);
cursor: default;
}
&__target-content,
&__add-btn {
line-height: 34px;
font-size: $small;
font-weight: $normal;
text-transform: none;
}
&__target-content {
@include flex-grow(1);
text-align: left;
padding-right: $pad-half;
}
&__count {
background-color: $text-medium;
border-radius: $border-radius;
color: $white;
font-size: $small;
font-size: 13px;
font-weight: bold;
padding: 5px;
margin-left: $pad-half;
}
&__delimeter {
@@ -24,25 +52,24 @@
&__icon {
color: $text-medium;
font-size: $base;
margin: 0 0 0 $pad-base;
font-size: $small;
}
&__ip {
color: $link;
color: $text-dark;
font-size: $small;
}
&__label-host {
color: $link;
font-size: $base;
margin-left: $pad-xsmall;
margin-left: $pad-half;
}
&__label-label {
color: $text-medium;
font-size: $base;
margin-left: $pad-xsmall;
font-size: 14px;
margin-left: $pad-half;
.all-hosts {
font-weight: $bold;
@@ -65,8 +92,4 @@
&__target-modal {
width: 1000px;
}
&__wrapper {
cursor: default;
}
}
@@ -1,26 +1,45 @@
.target-select {
&.Select {
.Select-control {
@include transition(border 150ms ease-in-out, box-shadow 150ms ease-in-out);
height: 48px;
}
.Select-arrow {
border-width: 8px 8px 2.5px;
}
.Select-input {
height: 46px;
margin: 0 0 0 $pad-base;
> input {
line-height: 30px;
padding: 8px 0;
color: $text-ultradark;
font-size: 16px;
}
}
.Select-placeholder {
line-height: 46px;
font-size: 16px;
padding: 0 $pad-base;
}
.Select-arrow-zone {
padding-right: 19px;
}
&.Select--multi {
.Select-option {
padding: 0;
background-color: $bg-light;
&.is-disabled {
&.is-focused {
background-color: $brand-light;
.target-option__label-label,
.target-option__target-icon,
.target-option__delimeter,
@@ -29,13 +48,163 @@
.target-option__icon {
color: $white;
}
.target-option__add-btn {
color: $brand-ultralight;
}
.target-option__count {
background-color: $white;
color: $brand;
}
}
}
&:last-child {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
}
.Select-value {
line-height: 1.9;
}
}
&.is-focused {
.Select-control {
border: 1px solid $accent-medium;
border-bottom-color: $brand;
box-shadow: inset 0 0 8px 0 rgba($black, 0.1);
}
}
}
.Select-menu-outer {
max-height: 500px;
height: 500px;
overflow: hidden;
box-shadow: 0 8px 12px 0 rgba(0, 0, 0, 0.15);
.Select-menu {
height: 498px;
max-height: 498px;
overflow: hidden;
position: relative;
&::after {
@extend %kolidecon;
content: '\f03c';
font-size: 150px;
position: absolute;
left: 57%;
top: 50%;
margin-top: -75px;
opacity: 0.25;
width: 43%;
text-align: center;
}
}
.target-list {
height: 100%;
}
}
.Select-value {
border-radius: 2px;
background-color: $white;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 2px 0 0 $accent-dark;
border: solid 1px $accent-dark;
.Select-value-icon {
border: 0;
float: right;
visibility: hidden;
position: relative;
padding: 0 $pad-medium;
line-height: 34px;
&::after {
@extend %kolidecon;
@include transition(color 150ms ease-in-out);
@include transform(translate(-50%, -50%));
content: '\f036';
position: absolute;
top: 50%;
left: 50%;
visibility: visible;
font-size: 20px;
color: $accent-dark;
}
&:hover {
&::after {
color: $accent-text;
}
}
}
.Select-value-label {
font-size: 16px;
font-weight: $normal;
color: $text-dark;
padding: 0 0 0 $pad-medium;
line-height: 34px;
}
}
.Select-clear {
font-size: 28px;
margin-right: 10px;
color: $text-dark;
&:hover {
color: $alert;
}
}
.Select-loading-zone {
padding-right: 10px;
}
&.is-empty {
.Select-menu-outer {
.Select-menu {
&::after {
content: '\f049';
font-size: 180px;
margin-top: -90px;
color: $accent-medium;
}
}
}
}
@include breakpoint(tablet) {
.Select-menu-outer {
.Select-menu {
min-width: 665px;
&::after {
display: none;
}
}
}
&.show-preview {
.Select-menu-outer {
.Select-menu {
left: auto;
right: 0;
}
}
.Select-menu {
left: 0;
right: auto;
position: absolute;
}
}
}
}
@@ -91,9 +91,9 @@ class QueryComposer extends Component {
return (
<div>
<p>
<p className={`${baseClass}__target-label`}>
<span className={`${baseClass}__select-targets`}>Select Targets</span>
<span className={`${baseClass}__targets-count`}> {selectedTargetsCount} unique hosts</span>
<span className={`${baseClass}__targets-count`}> {selectedTargetsCount} unique {selectedTargetsCount === 1 ? 'host' : 'hosts' }</span>
</p>
<SelectTargetsDropdown
onFetchTargets={onFetchTargets}
@@ -110,6 +110,7 @@ class QueryComposer extends Component {
return (
<div className={`${baseClass}__wrapper`}>
<h1>New Query</h1>
<div className={`${baseClass}__text-editor-wrapper`}>
<AceEditor
enableBasicAutocompletion
@@ -12,13 +12,21 @@
font-size: $base;
}
&__target-label {
margin: 0;
font-size: $base;
font-weight: $light;
color: $text-dark;
margin-bottom: $pad-xsmall;
}
&__targets-count {
font-size: $small;
margin-left: $xsmall;
}
&__text-editor-wrapper {
margin-top: $base;
margin: $base 0;
}
&__title {
@@ -30,5 +38,13 @@
&__wrapper {
background-color: $white;
padding: $base;
h1 {
font-size: $large;
font-weight: $light;
letter-spacing: 1.2px;
color: $text-medium;
margin: 0;
}
}
}
+191 -115
View File
@@ -1,6 +1,4 @@
[class^='kolidecon-'],
[class*=' kolidecon-'],
.kolidecon {
%kolidecon {
display: inline-block;
font: normal normal normal 14px/1 'kolidecons';
font-size: inherit;
@@ -9,228 +7,306 @@
-moz-osx-font-smoothing: grayscale;
}
[class^='kolidecon-'],
[class*=' kolidecon-'],
.kolidecon {
@extend %kolidecon;
}
.kolidecon-lg {
font-size: 1.33333333em;
line-height: 0.75em;
vertical-align: -15%;
font-size: 1.33333333em;
line-height: 0.75em;
vertical-align: -15%;
}
.kolidecon-2x {
font-size: 2em;
font-size: 2em;
}
.kolidecon-3x {
font-size: 3em;
font-size: 3em;
}
.kolidecon-4x {
font-size: 4em;
font-size: 4em;
}
.kolidecon-5x {
font-size: 5em;
font-size: 5em;
}
.kolidecon-fw {
width: 1.28571429em;
text-align: center;
.kolidecon-fw {
width: 1.28571429em;
text-align: center;
}
.kolidecon-kolide-logo-flat:before {
content: '\f000';
.kolidecon-kolide-logo-flat:before {
content: '\f000';
}
.kolidecon-chevrondown:before {
content: '\f004';
.kolidecon-chevrondown:before {
content: '\f004';
}
.kolidecon-chevronleft:before {
content: '\f006';
.kolidecon-chevronleft:before {
content: '\f006';
}
.kolidecon-chevronright:before {
content: '\f008';
.kolidecon-chevronright:before {
content: '\f008';
}
.kolidecon-chevronup:before {
content: '\f00a';
.kolidecon-chevronup:before {
content: '\f00a';
}
.kolidecon-cpu:before {
content: '\f00c';
.kolidecon-cpu:before {
content: '\f00c';
}
.kolidecon-downcarat:before {
content: '\f00d';
.kolidecon-downcarat:before {
content: '\f00d';
}
.kolidecon-filter:before {
content: '\f00f';
.kolidecon-filter:before {
content: '\f00f';
}
.kolidecon-mac:before {
content: '\f012';
.kolidecon-mac:before {
content: '\f012';
}
.kolidecon-memory:before {
content: '\f013';
.kolidecon-memory:before {
content: '\f013';
}
.kolidecon-penciledit:before {
content: '\f015';
.kolidecon-penciledit:before {
content: '\f015';
}
.kolidecon-storage:before {
content: '\f019';
.kolidecon-storage:before {
content: '\f019';
}
.kolidecon-upcarat:before {
content: '\f01b';
.kolidecon-upcarat:before {
content: '\f01b';
}
.kolidecon-uptime:before {
content: '\f01c';
.kolidecon-uptime:before {
content: '\f01c';
}
.kolidecon-world:before {
content: '\f01d';
.kolidecon-world:before {
content: '\f01d';
}
.kolidecon-osquery:before {
content: '\f021';
.kolidecon-osquery:before {
content: '\f021';
}
.kolidecon-join:before {
content: '\f022';
.kolidecon-join:before {
content: '\f022';
}
.kolidecon-add-button:before {
content: '\f029';
.kolidecon-add-button:before {
content: '\f029';
}
.kolidecon-username:before {
content: '\f02a';
.kolidecon-username:before {
content: '\f02a';
}
.kolidecon-password:before {
content: '\f02b';
.kolidecon-password:before {
content: '\f02b';
}
.kolidecon-email:before {
content: '\f02c';
.kolidecon-email:before {
content: '\f02c';
}
.kolidecon-query:before {
content: '\f02d';
.kolidecon-query:before {
content: '\f02d';
}
.kolidecon-hosts:before {
content: '\f02e';
.kolidecon-hosts:before {
content: '\f02e';
}
.kolidecon-packs:before {
content: '\f02f';
.kolidecon-packs:before {
content: '\f02f';
}
.kolidecon-help:before {
content: '\f030';
.kolidecon-help:before {
content: '\f030';
}
.kolidecon-admin:before {
content: '\f031';
.kolidecon-admin:before {
content: '\f031';
}
.kolidecon-config:before {
content: '\f032';
.kolidecon-config:before {
content: '\f032';
}
.kolidecon-mia:before {
content: '\f034';
.kolidecon-mia:before {
content: '\f034';
}
.kolidecon-success-check:before {
content: '\f035';
.kolidecon-success-check:before {
content: '\f035';
}
.kolidecon-offline:before {
content: '\f036';
.kolidecon-offline:before {
content: '\f036';
}
.kolidecon-windows:before {
content: '\f037';
.kolidecon-windows:before {
content: '\f037';
}
.kolidecon-centos:before {
content: '\f038';
.kolidecon-centos:before {
content: '\f038';
}
.kolidecon-ubuntu:before {
content: '\f039';
.kolidecon-ubuntu:before {
content: '\f039';
}
.kolidecon-apple:before {
content: '\f03a';
.kolidecon-apple:before {
content: '\f03a';
}
.kolidecon-search:before {
content: '\f03b';
.kolidecon-search:before {
content: '\f03b';
}
.kolidecon-all-hosts:before {
content: '\f03c';
.kolidecon-all-hosts:before {
content: '\f03c';
}
.kolidecon-single-host:before {
content: '\f03d';
.kolidecon-alerts:before {
content: '\f03e';
}
.kolidecon-alerts:before {
content: '\f03e';
.kolidecon-logout:before {
content: '\f03f';
}
.kolidecon-logout:before {
content: '\f03f';
.kolidecon-user-settings:before {
content: '\f040';
}
.kolidecon-user-settings:before {
content: '\f040';
.kolidecon-clipboard:before {
content: '\f043';
}
.kolidecon-clipboard:before {
content: '\f043';
.kolidecon-list-select:before {
content: '\f044';
}
.kolidecon-list-select:before {
content: '\f044';
.kolidecon-grid-select:before {
content: '\f045';
}
.kolidecon-grid-select:before {
content: '\f045';
.kolidecon-label:before {
content: '\f033';
}
.kolidecon-label:before {
content: '\f033';
.kolidecon-docker:before {
content: '\f046';
}
.kolidecon-cloud:before {
content: '\f047';
}
.kolidecon-self-hosted:before {
content: '\f048';
}
.kolidecon-help-solid:before {
content: '\f049';
}
.kolidecon-help-stroke:before {
content: '\f04a';
}
.kolidecon-warning-filled:before {
content: '\f04b';
}
.kolidecon-delete-cloud:before {
content: '\f04c';
}
.kolidecon-pdf:before {
content: '\f04d';
}
.kolidecon-credit-card-small:before {
content: '\f04e';
}
.kolidecon-billing-card:before {
content: '\f04f';
}
.kolidecon-lock-big:before {
content: '\f050';
}
.kolidecon-link-big:before {
content: '\f051';
}
.kolidecon-briefcase:before {
content: '\f052';
}
.kolidecon-name-card:before {
content: '\f053';
}
.kolidecon-kolide-logo:before {
content: '\f054';
}
.kolidecon-business:before {
content: '\f055';
}
.kolidecon-clock:before {
content: '\f056';
}
.kolidecon-host-large:before {
content: '\f057';
}
.kolidecon-single-host:before {
content: '\f03d';
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto
}
+13
View File
@@ -0,0 +1,13 @@
$min-width: 768px;
$medium-width: 1280px;
$max-width: 2560px;
@mixin breakpoint($size: desktop) {
@if ($size == tablet) {
@media (min-width: $min-width) and (max-width: $medium-width - 1) {
@content;
}
} @else {
@content;
}
}