update bootstrap to version 5 (#158)

* chore: update bootstrap to version 5

* chore: fix lint

* merge master

* update components to use bootstrap 5
This commit is contained in:
Juan Sebastian velez Posada
2024-01-15 08:56:17 -05:00
committed by GitHub
parent ca79adc4e5
commit 3406e1c647
19 changed files with 3469 additions and 3278 deletions

View File

@@ -1,48 +0,0 @@
@import 'node_modules/bootstrap/less/bootstrap';
@import 'node_modules/font-awesome/less/font-awesome';
@import 'bootswatch/paper/bootswatch';
@import 'bootswatch/paper/variables';
@import 'variables';
@import 'partials/_animations';
body {
min-width: 357px;
}
a.navbar-brand {
img {
margin-top: -12px;
float: left;
margin-right: 7px;
}
}
div.container {
margin-top: 20px;
}
canvas#icon {
display: none;
}
div#status {
display: none;
}
[type='text'].border-danger,
.border-danger {
box-shadow: inset 0 -2px 0 @brand-danger;
&:focus {
.box-shadow(inset 0 -2px 0 @brand-danger);
}
}
.space-between {
display: flex;
justify-content: space-between;
}
.align-items-center {
display: flex;
align-items: center;
}

View File

@@ -1,569 +0,0 @@
// Paper 3.3.4
// Bootswatch
// -----------------------------------------------------
@import url('http://fonts.googleapis.com/css?family=Roboto:300,400,500,700');
// Navbar =====================================================================
.navbar {
border: none;
.box-shadow(0 1px 2px rgba(0,0,0,0.3));
&-brand {
font-size: 24px;
}
&-inverse {
.form-control {
color: #fff;
.placeholder(@navbar-inverse-link-color);
&[type='text'] {
.box-shadow(inset 0 -1px 0 @navbar-inverse-link-color);
&:focus {
.box-shadow(inset 0 -2px 0 #fff);
}
}
}
}
&-nav > li > .dropdown-menu {
margin-top: 2px;
}
}
// Buttons ====================================================================
#btn(@class,@bg) {
.btn-@{class} {
background-size: 200%;
background-position: 50%;
&:hover,
&:active:hover,
&:focus {
background-color: darken(@bg, 6%);
}
&:active {
background-color: darken(@bg, 6%);
#gradient > .radial(darken(@bg, 6%) 10%, @bg 11%);;
background-size: 1000%;
.box-shadow(2px 2px 2px rgba(0,0,0,0.3));
}
}
}
#btn(default,@btn-default-bg);
#btn(primary,@btn-primary-bg);
#btn(success,@btn-success-bg);
#btn(info,@btn-info-bg);
#btn(warning,@btn-warning-bg);
#btn(danger,@btn-danger-bg);
.btn {
text-transform: uppercase;
border-right: none;
border-bottom: none;
.box-shadow(1px 1px 2px rgba(0,0,0,0.3));
.transition(all 0.2s);
&-link {
.box-shadow(none);
&:hover,
&:focus {
color: @brand-primary;
text-decoration: none;
}
}
&-default.disabled {
border: 1px solid @btn-default-border;
}
}
.btn-group {
.btn + .btn,
.btn + .btn-group,
.btn-group + .btn,
.btn-group + .btn-group {
margin-left: 0;
}
&-vertical {
> .btn + .btn,
> .btn + .btn-group,
> .btn-group + .btn,
> .btn-group + .btn-group {
margin-top: 0;
}
}
}
// Typography =================================================================
body {
-webkit-font-smoothing: antialiased;
letter-spacing: 0.1px;
text-rendering: optimizeLegibility;
}
p {
margin: 0 0 1em;
}
input,
button {
-webkit-font-smoothing: antialiased;
letter-spacing: 0.1px;
text-rendering: optimizeLegibility;
}
a {
.transition(all 0.2s);
}
// Tables =====================================================================
// Forms ======================================================================
label {
font-weight: normal;
}
textarea,
textarea.form-control,
input.form-control,
input[type='text'],
input[type='password'],
input[type='email'],
input[type='number'],
[type='text'].form-control,
[type='password'].form-control,
[type='email'].form-control,
[type='tel'].form-control,
[contenteditable].form-control {
padding: 0;
border: none;
border-radius: 0;
-webkit-appearance: none;
.box-shadow(inset 0 -1px 0 #ddd);
font-size: 16px;
&:focus {
.box-shadow(inset 0 -2px 0 @brand-primary);
}
&[disabled],
&[readonly] {
.box-shadow(none);
border-bottom: 1px dotted #ddd;
}
&.input {
&-sm {
font-size: @font-size-small;
}
&-lg {
font-size: @font-size-large;
}
}
}
select,
select.form-control {
border: 0;
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding-left: 0;
padding-right: 0\9; // remove padding for < ie9 since default arrow can't be removed
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEVmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmaP/QSjAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);
background-size: 13px;
background-repeat: no-repeat;
background-position: right center;
.box-shadow(inset 0 -1px 0 #ddd);
font-size: 16px;
line-height: 1.5;
&::-ms-expand {
display: none;
}
&.input {
&-sm {
font-size: @font-size-small;
}
&-lg {
font-size: @font-size-large;
}
}
&:focus {
.box-shadow(inset 0 -2px 0 @brand-primary);
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEUhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISF8S9ewAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);
}
&[multiple] {
background: none;
}
}
.radio,
.radio-inline,
.checkbox,
.checkbox-inline {
label {
padding-left: 25px;
}
input[type='radio'],
input[type='checkbox'] {
margin-left: -25px;
}
}
input[type='radio'],
.radio input[type='radio'],
.radio-inline input[type='radio'] {
position: relative;
margin-top: 5px;
margin-right: 4px;
vertical-align: -4px;
border: none;
background-color: transparent;
-webkit-appearance: none;
appearance: none;
cursor: pointer;
&:focus {
outline: none;
}
&:before,
&:after {
content: '';
display: block;
width: 18px;
height: 18px;
margin-top: -3px;
border-radius: 50%;
.transition(240ms);
}
&:before {
position: absolute;
left: 0;
top: 0;
background-color: @brand-primary;
.scale(0);
}
&:after {
border: 2px solid @gray;
}
&:checked:before {
.scale(0.5);
}
&:disabled:checked:before {
background-color: @gray-light;
}
&:checked:after {
border-color: @brand-primary;
}
&:disabled:after,
&:disabled:checked:after {
border-color: @gray-light;
}
}
input[type='checkbox'],
.checkbox input[type='checkbox'],
.checkbox-inline input[type='checkbox'] {
position: relative;
vertical-align: -4px;
border: none;
-webkit-appearance: none;
appearance: none;
cursor: pointer;
&:focus {
outline: none;
}
&:after {
content: '';
display: block;
width: 18px;
height: 18px;
margin-top: -2px;
margin-right: 5px;
border: 2px solid @gray;
border-radius: 2px;
.transition(240ms);
}
&:checked:before {
content: '';
position: absolute;
top: 0;
left: 6px;
display: table;
width: 6px;
height: 12px;
border: 2px solid #fff;
border-top-width: 0;
border-left-width: 0;
.rotate(45deg);
}
&:checked:after {
background-color: @brand-primary;
border-color: @brand-primary;
}
&:disabled:after {
border-color: @gray-light;
}
&:disabled:checked:after {
background-color: @gray-light;
border-color: transparent;
}
}
.has-warning {
input:not([type='checkbox']),
.form-control,
input:not([type='checkbox']):focus,
.form-control:focus {
.box-shadow(inset 0 -2px 0 @brand-warning);
}
}
.has-error {
input:not([type='checkbox']),
.form-control,
input:not([type='checkbox']):focus,
.form-control:focus {
.box-shadow(inset 0 -2px 0 @brand-danger);
}
}
.has-success {
input:not([type='checkbox']),
.form-control,
input:not([type='checkbox']):focus,
.form-control:focus {
.box-shadow(inset 0 -2px 0 @brand-success);
}
}
// Navs =======================================================================
.nav-tabs {
> li > a,
> li > a:focus {
margin-right: 0;
background-color: transparent;
border: none;
color: @navbar-default-link-color;
.box-shadow(inset 0 -1px 0 #ddd);
.transition(all 0.2s);
&:hover {
background-color: transparent;
.box-shadow(inset 0 -2px 0 @brand-primary);
color: @brand-primary;
}
}
& > li.active > a,
& > li.active > a:focus {
border: none;
.box-shadow(inset 0 -2px 0 @brand-primary);
color: @brand-primary;
&:hover {
border: none;
color: @brand-primary;
}
}
& > li.disabled > a {
.box-shadow(inset 0 -1px 0 #ddd);
}
&.nav-justified {
& > li > a,
& > li > a:hover,
& > .active > a,
& > .active > a:hover {
border: none;
}
}
.dropdown-menu {
margin-top: 0;
}
}
.dropdown-menu {
border: none;
.box-shadow(0 1px 4px rgba(0,0,0,0.3));
}
// Indicators =================================================================
.alert {
border: none;
color: #fff;
&-success {
background-color: @brand-success;
}
&-info {
background-color: @brand-info;
}
&-warning {
background-color: @brand-warning;
}
&-danger {
background-color: @brand-danger;
}
a:not(.close),
.alert-link {
color: #fff;
font-weight: bold;
}
.close {
color: #fff;
}
}
.badge {
padding: 3px 6px 5px;
}
.progress {
position: relative;
z-index: 1;
height: 6px;
border-radius: 0;
.box-shadow(none);
&-bar {
.box-shadow(none);
&:last-child {
border-radius: 0 3px 3px 0;
}
&:last-child {
&:before {
display: block;
content: '';
position: absolute;
width: 100%;
height: 100%;
left: 0;
right: 0;
z-index: -1;
background-color: lighten(@progress-bar-bg, 35%);
}
}
&-success:last-child.progress-bar:before {
background-color: lighten(@brand-success, 35%);
}
&-info:last-child.progress-bar:before {
background-color: lighten(@brand-info, 45%);
}
&-warning:last-child.progress-bar:before {
background-color: lighten(@brand-warning, 35%);
}
&-danger:last-child.progress-bar:before {
background-color: lighten(@brand-danger, 25%);
}
}
}
// Progress bars ==============================================================
// Containers =================================================================
.close {
font-size: 34px;
font-weight: 300;
line-height: 24px;
opacity: 0.6;
&:hover {
opacity: 1;
}
}
.list-group {
&-item {
padding: 15px;
}
&-item-text {
color: @gray-light;
}
}
.well {
border-radius: 0;
.box-shadow(none);
}
.panel {
border: none;
border-radius: 2px;
.box-shadow(0 1px 4px rgba(0,0,0,0.3));
&-heading {
border-bottom: none;
}
&-footer {
border-top: none;
}
}
.popover {
border: none;
.box-shadow(0 1px 4px rgba(0,0,0,0.3));
}
.carousel {
&-caption {
h1,
h2,
h3,
h4,
h5,
h6 {
color: inherit;
}
}
}

View File

@@ -1,829 +0,0 @@
// Paper 3.3.4
// Variables
// --------------------------------------------------
//== Colors
//
//## Gray and brand colors for use across Bootstrap.
@gray-base: #000;
@gray-darker: lighten(@gray-base, 13.5%); // #222
@gray-dark: #212121;
@gray: #666;
@gray-light: #bbb;
@gray-lighter: lighten(@gray-base, 93.5%); // #eee
@brand-primary: #2196f3;
@brand-success: #4caf50;
@brand-info: #9c27b0;
@brand-warning: #ff9800;
@brand-danger: #e51c23;
//== Scaffolding
//
//## Settings for some of the most global styles.
//** Background color for `<body>`.
@body-bg: #fff;
//** Global text color on `<body>`.
@text-color: @gray;
//** Global textual link color.
@link-color: @brand-primary;
//** Link hover color set via `darken()` function.
@link-hover-color: darken(@link-color, 15%);
//** Link hover decoration.
@link-hover-decoration: underline;
//== Typography
//
//## Font, line-height, and color for body text, headings, and more.
@font-family-sans-serif: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
@font-family-serif: Georgia, 'Times New Roman', Times, serif;
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
@font-family-monospace: Menlo, Monaco, Consolas, 'Courier New', monospace;
@font-family-base: @font-family-sans-serif;
@font-size-base: 13px;
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
@font-size-h1: 56px;
@font-size-h2: 45px;
@font-size-h3: 34px;
@font-size-h4: 24px;
@font-size-h5: 20px;
@font-size-h6: 14px;
//** Unit-less `line-height` for use in components like buttons.
@line-height-base: 1.846; // 20/14
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
//** By default, this inherits from the `<body>`.
@headings-font-family: inherit;
@headings-font-weight: 400;
@headings-line-height: 1.1;
@headings-color: #444;
//== Iconography
//
//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
//** Load fonts from this directory.
@icon-font-path: '../fonts/';
//** File name for all font files.
@icon-font-name: 'glyphicons-halflings-regular';
//** Element ID within SVG icon file.
@icon-font-svg-id: 'glyphicons_halflingsregular';
//== Components
//
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
@padding-base-vertical: 6px;
@padding-base-horizontal: 16px;
@padding-large-vertical: 10px;
@padding-large-horizontal: 16px;
@padding-small-vertical: 5px;
@padding-small-horizontal: 10px;
@padding-xs-vertical: 1px;
@padding-xs-horizontal: 5px;
@line-height-large: 1.3333333; // extra decimals for Win 8.1 Chrome
@line-height-small: 1.5;
@border-radius-base: 3px;
@border-radius-large: 3px;
@border-radius-small: 3px;
//** Global color for active items (e.g., navs or dropdowns).
@component-active-color: #fff;
//** Global background color for active items (e.g., navs or dropdowns).
@component-active-bg: @brand-primary;
//** Width of the `border` for generating carets that indicator dropdowns.
@caret-width-base: 4px;
//** Carets increase slightly in size for larger components.
@caret-width-large: 5px;
//== Tables
//
//## Customizes the `.table` component with basic values, each used across all table variations.
//** Padding for `<th>`s and `<td>`s.
@table-cell-padding: 8px;
//** Padding for cells in `.table-condensed`.
@table-condensed-cell-padding: 5px;
//** Default background color used for all tables.
@table-bg: transparent;
//** Background color used for `.table-striped`.
@table-bg-accent: #f9f9f9;
//** Background color used for `.table-hover`.
@table-bg-hover: #f5f5f5;
@table-bg-active: @table-bg-hover;
//** Border color for table and cell borders.
@table-border-color: #ddd;
//== Buttons
//
//## For each of Bootstrap's buttons, define text, background and border color.
@btn-font-weight: normal;
@btn-default-color: @text-color;
@btn-default-bg: #fff;
@btn-default-border: #eee;
@btn-primary-color: #fff;
@btn-primary-bg: @brand-primary;
@btn-primary-border: transparent;
@btn-success-color: #fff;
@btn-success-bg: @brand-success;
@btn-success-border: transparent;
@btn-info-color: #fff;
@btn-info-bg: @brand-info;
@btn-info-border: transparent;
@btn-warning-color: #fff;
@btn-warning-bg: @brand-warning;
@btn-warning-border: transparent;
@btn-danger-color: #fff;
@btn-danger-bg: @brand-danger;
@btn-danger-border: transparent;
@btn-link-disabled-color: @gray-light;
//== Forms
//
//##
//** `<input>` background color
@input-bg: transparent;
//** `<input disabled>` background color
@input-bg-disabled: transparent;
//** Text color for `<input>`s
@input-color: @gray;
//** `<input>` border color
@input-border: transparent;
// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
//** Default `.form-control` border radius
// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS.
@input-border-radius: @border-radius-base;
//** Large `.form-control` border radius
@input-border-radius-large: @border-radius-large;
//** Small `.form-control` border radius
@input-border-radius-small: @border-radius-small;
//** Border color for inputs on focus
@input-border-focus: #66afe9;
//** Placeholder text color
@input-color-placeholder: @gray-light;
//** Default `.form-control` height
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
//** Large `.form-control` height
@input-height-large: (
ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2
);
//** Small `.form-control` height
@input-height-small: (
floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2
);
//** `.form-group` margin
@form-group-margin-bottom: 15px;
@legend-color: @gray-dark;
@legend-border-color: #e5e5e5;
//** Background color for textual input addons
@input-group-addon-bg: transparent;
//** Border color for textual input addons
@input-group-addon-border-color: @input-border;
//** Disabled cursor for form controls and buttons.
@cursor-disabled: not-allowed;
//== Dropdowns
//
//## Dropdown menu container and contents.
//** Background for the dropdown menu.
@dropdown-bg: #fff;
//** Dropdown menu `border-color`.
@dropdown-border: rgba(0, 0, 0, 0.15);
//** Dropdown menu `border-color` **for IE8**.
@dropdown-fallback-border: #ccc;
//** Divider color for between dropdown items.
@dropdown-divider-bg: #e5e5e5;
//** Dropdown link text color.
@dropdown-link-color: @text-color;
//** Hover color for dropdown links.
@dropdown-link-hover-color: darken(@gray-dark, 5%);
//** Hover background for dropdown links.
@dropdown-link-hover-bg: @gray-lighter;
//** Active dropdown menu item text color.
@dropdown-link-active-color: @component-active-color;
//** Active dropdown menu item background color.
@dropdown-link-active-bg: @component-active-bg;
//** Disabled dropdown menu item background color.
@dropdown-link-disabled-color: @gray-light;
//** Text color for headers within dropdown menus.
@dropdown-header-color: @gray-light;
//** Deprecated `@dropdown-caret-color` as of v3.1.0
@dropdown-caret-color: @gray-light;
//-- Z-index master list
//
// Warning: Avoid customizing these values. They're used for a bird's eye view
// of components dependent on the z-axis and are designed to all work together.
//
// Note: These variables are not generated into the Customizer.
@zindex-navbar: 1000;
@zindex-dropdown: 1000;
@zindex-popover: 1060;
@zindex-tooltip: 1070;
@zindex-navbar-fixed: 1030;
@zindex-modal-background: 1040;
@zindex-modal: 1050;
//== Media queries breakpoints
//
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
// Extra small screen / phone
//** Deprecated `@screen-xs` as of v3.0.1
@screen-xs: 480px;
//** Deprecated `@screen-xs-min` as of v3.2.0
@screen-xs-min: @screen-xs;
//** Deprecated `@screen-phone` as of v3.0.1
@screen-phone: @screen-xs-min;
// Small screen / tablet
//** Deprecated `@screen-sm` as of v3.0.1
@screen-sm: 768px;
@screen-sm-min: @screen-sm;
//** Deprecated `@screen-tablet` as of v3.0.1
@screen-tablet: @screen-sm-min;
// Medium screen / desktop
//** Deprecated `@screen-md` as of v3.0.1
@screen-md: 992px;
@screen-md-min: @screen-md;
//** Deprecated `@screen-desktop` as of v3.0.1
@screen-desktop: @screen-md-min;
// Large screen / wide desktop
//** Deprecated `@screen-lg` as of v3.0.1
@screen-lg: 1200px;
@screen-lg-min: @screen-lg;
//** Deprecated `@screen-lg-desktop` as of v3.0.1
@screen-lg-desktop: @screen-lg-min;
// So media queries don't overlap when required, provide a maximum
@screen-xs-max: (@screen-sm-min - 1);
@screen-sm-max: (@screen-md-min - 1);
@screen-md-max: (@screen-lg-min - 1);
//== Grid system
//
//## Define your custom responsive grid.
//** Number of columns in the grid.
@grid-columns: 12;
//** Padding between columns. Gets divided in half for the left and right.
@grid-gutter-width: 30px;
// Navbar collapse
//** Point at which the navbar becomes uncollapsed.
@grid-float-breakpoint: @screen-sm-min;
//** Point at which the navbar begins collapsing.
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
//== Container sizes
//
//## Define the maximum width of `.container` for different screen sizes.
// Small screen / tablet
@container-tablet: (720px + @grid-gutter-width);
//** For `@screen-sm-min` and up.
@container-sm: @container-tablet;
// Medium screen / desktop
@container-desktop: (940px + @grid-gutter-width);
//** For `@screen-md-min` and up.
@container-md: @container-desktop;
// Large screen / wide desktop
@container-large-desktop: (1140px + @grid-gutter-width);
//** For `@screen-lg-min` and up.
@container-lg: @container-large-desktop;
//== Navbar
//
//##
// Basics of a navbar
@navbar-height: 64px;
@navbar-margin-bottom: @line-height-computed;
@navbar-border-radius: @border-radius-base;
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
@navbar-collapse-max-height: 340px;
@navbar-default-color: @gray-light;
@navbar-default-bg: #fff;
@navbar-default-border: transparent;
// Navbar links
@navbar-default-link-color: @gray;
@navbar-default-link-hover-color: @gray-dark;
@navbar-default-link-hover-bg: transparent;
@navbar-default-link-active-color: @gray-dark;
@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
@navbar-default-link-disabled-color: #ccc;
@navbar-default-link-disabled-bg: transparent;
// Navbar brand label
@navbar-default-brand-color: @navbar-default-link-color;
@navbar-default-brand-hover-color: @navbar-default-link-hover-color;
@navbar-default-brand-hover-bg: transparent;
// Navbar toggle
@navbar-default-toggle-hover-bg: transparent;
@navbar-default-toggle-icon-bar-bg: rgba(0, 0, 0, 0.5);
@navbar-default-toggle-border-color: transparent;
// Inverted navbar
// Reset inverted navbar basics
@navbar-inverse-color: @gray-light;
@navbar-inverse-bg: @brand-primary;
@navbar-inverse-border: transparent;
// Inverted navbar links
@navbar-inverse-link-color: lighten(@brand-primary, 30%);
@navbar-inverse-link-hover-color: #fff;
@navbar-inverse-link-hover-bg: transparent;
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
@navbar-inverse-link-disabled-color: #444;
@navbar-inverse-link-disabled-bg: transparent;
// Inverted navbar brand label
@navbar-inverse-brand-color: @navbar-inverse-link-color;
@navbar-inverse-brand-hover-color: #fff;
@navbar-inverse-brand-hover-bg: transparent;
// Inverted navbar toggle\
@navbar-inverse-toggle-hover-bg: transparent;
@navbar-inverse-toggle-icon-bar-bg: rgba(0, 0, 0, 0.5);
@navbar-inverse-toggle-border-color: transparent;
//== Navs
//
//##
//=== Shared nav styles
@nav-link-padding: 10px 15px;
@nav-link-hover-bg: @gray-lighter;
@nav-disabled-link-color: @gray-light;
@nav-disabled-link-hover-color: @gray-light;
//== Tabs
@nav-tabs-border-color: transparent;
@nav-tabs-link-hover-border-color: @gray-lighter;
@nav-tabs-active-link-hover-bg: transparent;
@nav-tabs-active-link-hover-color: @gray;
@nav-tabs-active-link-hover-border-color: transparent;
@nav-tabs-justified-link-border-color: @nav-tabs-border-color;
@nav-tabs-justified-active-link-border-color: @body-bg;
//== Pills
@nav-pills-border-radius: @border-radius-base;
@nav-pills-active-link-hover-bg: @component-active-bg;
@nav-pills-active-link-hover-color: @component-active-color;
//== Pagination
//
//##
@pagination-color: @link-color;
@pagination-bg: #fff;
@pagination-border: #ddd;
@pagination-hover-color: @link-hover-color;
@pagination-hover-bg: @gray-lighter;
@pagination-hover-border: #ddd;
@pagination-active-color: #fff;
@pagination-active-bg: @brand-primary;
@pagination-active-border: @brand-primary;
@pagination-disabled-color: @gray-light;
@pagination-disabled-bg: #fff;
@pagination-disabled-border: #ddd;
//== Pager
//
//##
@pager-bg: @pagination-bg;
@pager-border: @pagination-border;
@pager-border-radius: 15px;
@pager-hover-bg: @pagination-hover-bg;
@pager-active-bg: @pagination-active-bg;
@pager-active-color: @pagination-active-color;
@pager-disabled-color: @pagination-disabled-color;
//== Jumbotron
//
//##
@jumbotron-padding: 30px;
@jumbotron-color: inherit;
@jumbotron-bg: #f9f9f9;
@jumbotron-heading-color: @headings-color;
@jumbotron-font-size: ceil((@font-size-base * 1.5));
//== Form states and alerts
//
//## Define colors for form feedback states and, by default, alerts.
@state-success-text: @brand-success;
@state-success-bg: #dff0d8;
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
@state-info-text: @brand-info;
@state-info-bg: #e1bee7;
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
@state-warning-text: @brand-warning;
@state-warning-bg: #ffe0b2;
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
@state-danger-text: @brand-danger;
@state-danger-bg: #f9bdbb;
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
//== Tooltips
//
//##
//** Tooltip max width
@tooltip-max-width: 200px;
//** Tooltip text color
@tooltip-color: #fff;
//** Tooltip background color
@tooltip-bg: #727272;
@tooltip-opacity: 0.9;
//** Tooltip arrow width
@tooltip-arrow-width: 5px;
//** Tooltip arrow color
@tooltip-arrow-color: @tooltip-bg;
//== Popovers
//
//##
//** Popover body background color
@popover-bg: #fff;
//** Popover maximum width
@popover-max-width: 276px;
//** Popover border color
@popover-border-color: transparent;
//** Popover fallback border color
@popover-fallback-border-color: transparent;
//** Popover title background color
@popover-title-bg: darken(@popover-bg, 3%);
//** Popover arrow width
@popover-arrow-width: 10px;
//** Popover arrow color
@popover-arrow-color: @popover-bg;
//** Popover outer arrow width
@popover-arrow-outer-width: (@popover-arrow-width + 1);
//** Popover outer arrow color
@popover-arrow-outer-color: fadein(@popover-border-color, 7.5%);
//** Popover outer arrow fallback color
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
//== Labels
//
//##
//** Default label background color
@label-default-bg: @gray-light;
//** Primary label background color
@label-primary-bg: @brand-primary;
//** Success label background color
@label-success-bg: @brand-success;
//** Info label background color
@label-info-bg: @brand-info;
//** Warning label background color
@label-warning-bg: @brand-warning;
//** Danger label background color
@label-danger-bg: @brand-danger;
//** Default label text color
@label-color: #fff;
//** Default text color of a linked label
@label-link-hover-color: #fff;
//== Modals
//
//##
//** Padding applied to the modal body
@modal-inner-padding: 15px;
//** Padding applied to the modal title
@modal-title-padding: 15px;
//** Modal title line-height
@modal-title-line-height: @line-height-base;
//** Background color of modal content area
@modal-content-bg: #fff;
//** Modal content border color
@modal-content-border-color: transparent;
//** Modal content border color **for IE8**
@modal-content-fallback-border-color: #999;
//** Modal backdrop background color
@modal-backdrop-bg: #000;
//** Modal backdrop opacity
@modal-backdrop-opacity: 0.5;
//** Modal header border color
@modal-header-border-color: transparent;
//** Modal footer border color
@modal-footer-border-color: @modal-header-border-color;
@modal-lg: 900px;
@modal-md: 600px;
@modal-sm: 300px;
//== Alerts
//
//## Define alert colors, border radius, and padding.
@alert-padding: 15px;
@alert-border-radius: @border-radius-base;
@alert-link-font-weight: bold;
@alert-success-bg: @state-success-bg;
@alert-success-text: @state-success-text;
@alert-success-border: @state-success-border;
@alert-info-bg: @state-info-bg;
@alert-info-text: @state-info-text;
@alert-info-border: @state-info-border;
@alert-warning-bg: @state-warning-bg;
@alert-warning-text: @state-warning-text;
@alert-warning-border: @state-warning-border;
@alert-danger-bg: @state-danger-bg;
@alert-danger-text: @state-danger-text;
@alert-danger-border: @state-danger-border;
//== Progress bars
//
//##
//** Background color of the whole progress component
@progress-bg: #f5f5f5;
//** Progress bar text color
@progress-bar-color: #fff;
//** Variable for setting rounded corners on progress bar.
@progress-border-radius: @border-radius-base;
//** Default progress bar color
@progress-bar-bg: @brand-primary;
//** Success progress bar color
@progress-bar-success-bg: @brand-success;
//** Warning progress bar color
@progress-bar-warning-bg: @brand-warning;
//** Danger progress bar color
@progress-bar-danger-bg: @brand-danger;
//** Info progress bar color
@progress-bar-info-bg: @brand-info;
//== List group
//
//##
//** Background color on `.list-group-item`
@list-group-bg: #fff;
//** `.list-group-item` border color
@list-group-border: #ddd;
//** List group border radius
@list-group-border-radius: @border-radius-base;
//** Background color of single list items on hover
@list-group-hover-bg: #f5f5f5;
//** Text color of active list items
@list-group-active-color: @component-active-color;
//** Background color of active list items
@list-group-active-bg: @component-active-bg;
//** Border color of active list elements
@list-group-active-border: @list-group-active-bg;
//** Text color for content within active list items
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
//** Text color of disabled list items
@list-group-disabled-color: @gray-light;
//** Background color of disabled list items
@list-group-disabled-bg: @gray-lighter;
//** Text color for content within disabled list items
@list-group-disabled-text-color: @list-group-disabled-color;
@list-group-link-color: #555;
@list-group-link-hover-color: @list-group-link-color;
@list-group-link-heading-color: #333;
//== Panels
//
//##
@panel-bg: #fff;
@panel-body-padding: 15px;
@panel-heading-padding: 10px 15px;
@panel-footer-padding: @panel-heading-padding;
@panel-border-radius: @border-radius-base;
//** Border color for elements within panels
@panel-inner-border: #ddd;
@panel-footer-bg: #f5f5f5;
@panel-default-text: @gray-dark;
@panel-default-border: #ddd;
@panel-default-heading-bg: #f5f5f5;
@panel-primary-text: #fff;
@panel-primary-border: @brand-primary;
@panel-primary-heading-bg: @brand-primary;
@panel-success-text: #fff;
@panel-success-border: @state-success-border;
@panel-success-heading-bg: @brand-success;
@panel-info-text: #fff;
@panel-info-border: @state-info-border;
@panel-info-heading-bg: @brand-info;
@panel-warning-text: #fff;
@panel-warning-border: @state-warning-border;
@panel-warning-heading-bg: @brand-warning;
@panel-danger-text: #fff;
@panel-danger-border: @state-danger-border;
@panel-danger-heading-bg: @brand-danger;
//== Thumbnails
//
//##
//** Padding around the thumbnail image
@thumbnail-padding: 4px;
//** Thumbnail background color
@thumbnail-bg: @body-bg;
//** Thumbnail border color
@thumbnail-border: #ddd;
//** Thumbnail border radius
@thumbnail-border-radius: @border-radius-base;
//** Custom text color for thumbnail captions
@thumbnail-caption-color: @text-color;
//** Padding around the thumbnail caption
@thumbnail-caption-padding: 9px;
//== Wells
//
//##
@well-bg: #f9f9f9;
@well-border: transparent;
//== Badges
//
//##
@badge-color: #fff;
//** Linked badge text color on hover
@badge-link-hover-color: #fff;
@badge-bg: @gray-light;
//** Badge text color in active nav link
@badge-active-color: @link-color;
//** Badge background color in active nav link
@badge-active-bg: #fff;
@badge-font-weight: normal;
@badge-line-height: 1;
@badge-border-radius: 10px;
//== Breadcrumbs
//
//##
@breadcrumb-padding-vertical: 8px;
@breadcrumb-padding-horizontal: 15px;
//** Breadcrumb background color
@breadcrumb-bg: #f5f5f5;
//** Breadcrumb text color
@breadcrumb-color: #ccc;
//** Text color of current page in the breadcrumb
@breadcrumb-active-color: @gray-light;
//** Textual separator for between breadcrumb elements
@breadcrumb-separator: '/';
//== Carousel
//
//##
@carousel-text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
@carousel-control-color: #fff;
@carousel-control-width: 15%;
@carousel-control-opacity: 0.5;
@carousel-control-font-size: 20px;
@carousel-indicator-active-bg: #fff;
@carousel-indicator-border-color: #fff;
@carousel-caption-color: #fff;
//== Close
//
//##
@close-font-weight: normal;
@close-color: #000;
@close-text-shadow: none;
//== Code
//
//##
@code-color: #c7254e;
@code-bg: #f9f2f4;
@kbd-color: #fff;
@kbd-bg: #333;
@pre-bg: #f5f5f5;
@pre-color: @gray-dark;
@pre-border-color: #ccc;
@pre-scrollable-max-height: 340px;
//== Type
//
//##
//** Horizontal offset for forms and lists.
@component-offset-horizontal: 180px;
//** Text muted color
@text-muted: @gray-light;
//** Abbreviations and acronyms border color
@abbr-border-color: @gray-light;
//** Headings small color
@headings-small-color: @gray-light;
//** Blockquote small color
@blockquote-small-color: @gray-light;
//** Blockquote font size
@blockquote-font-size: (@font-size-base * 1.25);
//** Blockquote border color
@blockquote-border-color: @gray-lighter;
//** Page header border color
@page-header-border-color: @gray-lighter;
//** Width of horizontal description list titles
@dl-horizontal-offset: @component-offset-horizontal;
//** Horizontal line color.
@hr-border: @gray-lighter;

View File

@@ -1,14 +0,0 @@
::-webkit-scrollbar {
height: 12px;
width: 12px;
background: @body-bg;
}
::-webkit-scrollbar-thumb {
background: @brand-primary;
-webkit-border-radius: 0;
}
::-webkit-scrollbar-corner {
background: #000;
}

View File

@@ -1,2 +0,0 @@
@navbar-margin-bottom: 0px;
@navbar-border-radius: 0px;

51
assets/sass/app.scss Normal file
View File

@@ -0,0 +1,51 @@
@import 'node_modules/bootswatch/dist/materia/_variables';
@import 'node_modules/bootstrap/scss/bootstrap';
@import 'node_modules/bootswatch/dist/materia/_bootswatch';
@import 'node_modules/font-awesome/scss/font-awesome';
@import 'variables';
@import 'partials/_animations';
body {
min-width: 357px;
}
a.navbar-brand {
display: flex;
align-items: flex-end;
height: 60px;
img {
margin-top: -12px;
margin-right: 7px;
}
}
div.container {
margin-top: 20px;
}
canvas#icon {
display: none;
}
div#status {
display: none;
}
// [type='text'].border-danger,
// .border-danger {
// box-shadow: inset 0 -2px 0 red;
// &:focus {
// .box-shadow(inset 0 -2px 0 #F3F4F5);
// }
// }
.space-between {
display: flex;
justify-content: space-between;
}
.align-items-center {
display: flex;
align-items: center;
}

View File

@@ -0,0 +1,2 @@
$navbar-margin-bottom: 0px;
$navbar-border-radius: 0px;

4606
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,8 @@
"dependencies": { "dependencies": {
"@manaflair/redux-batch": "^1.0.0", "@manaflair/redux-batch": "^1.0.0",
"@reduxjs/toolkit": "^1.9.0", "@reduxjs/toolkit": "^1.9.0",
"bootstrap": "3.4.1", "bootstrap": "^5.3.2",
"bootswatch": "^5.2.3",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"create-react-class": "^15.7.0", "create-react-class": "^15.7.0",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
@@ -53,6 +54,7 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^12.6.0", "@testing-library/user-event": "^12.6.0",
"@types/bootstrap": "^5.2.10",
"@types/chai": "^4.3.4", "@types/chai": "^4.3.4",
"@types/chrome": "0.0.128", "@types/chrome": "0.0.128",
"@types/classnames": "^2.2.11", "@types/classnames": "^2.2.11",
@@ -97,11 +99,11 @@
"jsdom": "^21.0.0", "jsdom": "^21.0.0",
"jshint": "^2.13.6", "jshint": "^2.13.6",
"jsxhint": "^0.15.1", "jsxhint": "^0.15.1",
"less": "^4.1.3",
"lint-staged": "^13.1.0", "lint-staged": "^13.1.0",
"mocha": "^10.2.0", "mocha": "^10.2.0",
"mocha-sinon": "^2.1.2", "mocha-sinon": "^2.1.2",
"node-gyp": "^8.3.0", "node-gyp": "^8.3.0",
"node-sass": "^8.0.0",
"prettier": "^2.8.2", "prettier": "^2.8.2",
"prettier-plugin-packagejson": "^2.3.0", "prettier-plugin-packagejson": "^2.3.0",
"prettier-plugin-sort-json": "1.0.0", "prettier-plugin-sort-json": "1.0.0",
@@ -115,7 +117,7 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.9.4", "typescript": "^4.9.4",
"wait-on": "^7.0.1", "wait-on": "^7.0.1",
"web-ext": "^7.4.0", "web-ext": "^7.10.0",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-cli": "^5.0.1" "webpack-cli": "^5.0.1"
} }

View File

@@ -30,22 +30,22 @@ describe('MainList', () => {
class="list-group" class="list-group"
> >
<a <a
class="list-group-item" class="list-group-item text-body-secondary"
href="#" href="#"
> >
<i <i
class="fa fa-fw fa-cogs" class="fa fa-fw fa-cogs me-2"
/> />
Options Options
</a> </a>
<a <a
class="list-group-item" class="list-group-item text-body-secondary"
href="https://wakatime.com/login" href="https://wakatime.com/login"
rel="noreferrer" rel="noreferrer"
target="_blank" target="_blank"
> >
<i <i
class="fa fa-fw fa-sign-in" class="fa fa-fw fa-sign-in me-2"
/> />
Login Login
</a> </a>

View File

@@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import { useSelector, useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import changeExtensionState from '../utils/changeExtensionState';
import { configLogout, setLoggingEnabled } from '../reducers/configReducer'; import { configLogout, setLoggingEnabled } from '../reducers/configReducer';
import { userLogout } from '../reducers/currentUser'; import { userLogout } from '../reducers/currentUser';
import { ReduxSelector } from '../types/store'; import { ReduxSelector } from '../types/store';
import { User } from '../types/user'; import { User } from '../types/user';
import changeExtensionState from '../utils/changeExtensionState';
export interface MainListProps { export interface MainListProps {
loggingEnabled: boolean; loggingEnabled: boolean;
@@ -51,7 +51,7 @@ export default function MainList({
<blockquote> <blockquote>
<p>{totalTimeLoggedToday}</p> <p>{totalTimeLoggedToday}</p>
<small> <small>
<cite>TOTAL TIME LOGGED TODAY</cite> <cite className="text-body-secondary">TOTAL TIME LOGGED TODAY</cite>
</small> </small>
</blockquote> </blockquote>
</div> </div>
@@ -61,7 +61,11 @@ export default function MainList({
<div className="row"> <div className="row">
<div className="col-xs-12"> <div className="col-xs-12">
<p> <p>
<a href="#" onClick={disableLogging} className="btn btn-danger btn-block"> <a
href="#"
onClick={disableLogging}
className="btn btn-danger btn-block w-100 btn-sm"
>
Disable logging Disable logging
</a> </a>
</p> </p>
@@ -72,7 +76,11 @@ export default function MainList({
<div className="row"> <div className="row">
<div className="col-xs-12"> <div className="col-xs-12">
<p> <p>
<a href="#" onClick={enableLogging} className="btn btn-success btn-block"> <a
href="#"
onClick={enableLogging}
className="btn btn-success btn-block w-100 btn-sm"
>
Enable logging Enable logging
</a> </a>
</p> </p>
@@ -80,14 +88,14 @@ export default function MainList({
</div> </div>
)} )}
<div className="list-group"> <div className="list-group">
<a href="#" className="list-group-item" onClick={openOptionsPage}> <a href="#" className="list-group-item text-body-secondary" onClick={openOptionsPage}>
<i className="fa fa-fw fa-cogs"></i> <i className="fa fa-fw fa-cogs me-2"></i>
Options Options
</a> </a>
{user && ( {user && (
<div> <div>
<a href="#" className="list-group-item" onClick={logoutUser}> <a href="#" className="list-group-item text-body-secondary" onClick={logoutUser}>
<i className="fa fa-fw fa-sign-out"></i> <i className="fa fa-fw fa-sign-out me-2"></i>
Logout Logout
</a> </a>
</div> </div>
@@ -97,9 +105,9 @@ export default function MainList({
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
href="https://wakatime.com/login" href="https://wakatime.com/login"
className="list-group-item" className="list-group-item text-body-secondary"
> >
<i className="fa fa-fw fa-sign-in"></i> <i className="fa fa-fw fa-sign-in me-2"></i>
Login Login
</a> </a>
)} )}

View File

@@ -18,102 +18,109 @@ describe('NavBar', () => {
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
<nav <nav
class="navbar navbar-default" class="navbar shadow-none"
role="navigation" role="navigation"
> >
<div <div
class="container-fluid" class="navbar-header d-flex w-100 justify-content-between"
> >
<div <a
class="navbar-header" class="navbar-brand"
href="https://wakatime.com"
rel="noreferrer"
target="_blank"
> >
<button <img
class="navbar-toggle collapsed" src="graphics/wakatime-logo-48.png"
data-target="#bs-example-navbar-collapse-1" />
data-toggle="collapse" <div>
type="button"
>
<span
class="sr-only"
>
Toggle navigation
</span>
<i
class="fa fa-fw fa-cogs"
/>
</button>
<a
class="navbar-brand"
href="https://wakatime.com"
rel="noreferrer"
target="_blank"
>
WakaTime WakaTime
<img </div>
src="graphics/wakatime-logo-48.png" </a>
/> <button
</a> aria-controls="userInfoCollapse"
</div> aria-expanded="false"
<div aria-label="Toggle navigation"
class="collapse navbar-collapse" class="navbar-toggler"
id="bs-example-navbar-collapse-1" data-bs-target="#userInfoCollapse"
data-bs-toggle="collapse"
type="button"
>
<span
class="sr-only"
>
Toggle navigation
</span>
<i
class="fa fa-fw fa-cogs"
/>
</button>
</div>
<div
class="collapse navbar-collapse mt-4"
id="userInfoCollapse"
>
<div />
<ul
class="nav navbar-nav border-bottom pb-2"
> >
<div /> <div />
<ul <div />
class="nav navbar-nav" <li
class="dropdown"
> >
<div /> <a
<div /> aria-expanded="false"
<li class="dropdown-toggle text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
class="dropdown" data-bs-toggle="dropdown"
href="#"
role="button"
> >
<a <i
aria-expanded="false" class="fa fa-fw fa-info me-2"
class="dropdown-toggle" />
data-toggle="dropdown" About
href="#" <span
role="button" class="caret"
/>
</a>
<ul
class="dropdown-menu shadow-none ms-4"
role="menu"
>
<li
class="mb-2"
> >
<i <a
class="fa fa-fw fa-info" class="text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
/> href="https://github.com/wakatime/chrome-wakatime/issues"
About rel="noreferrer"
<span target="_blank"
class="caret" >
/> <i
</a> class="fa fa-fw fa-bug me-2"
<ul />
class="dropdown-menu" Report an Issue
role="menu" </a>
</li>
<li
class="mb-2"
> >
<li> <a
<a class="text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
href="https://github.com/wakatime/chrome-wakatime/issues" href="https://github.com/wakatime/chrome-wakatime"
rel="noreferrer" rel="noreferrer"
target="_blank" target="_blank"
> >
<i <i
class="fa fa-fw fa-bug" class="fa fa-fw fa-github me-2"
/> />
Report an Issue View on GitHub
</a> </a>
</li> </li>
<li> </ul>
<a </li>
href="https://github.com/wakatime/chrome-wakatime" </ul>
rel="noreferrer"
target="_blank"
>
<i
class="fa fa-fw fa-github"
/>
View on GitHub
</a>
</li>
</ul>
</li>
</ul>
</div>
</div> </div>
</nav> </nav>
</div> </div>

View File

@@ -11,7 +11,7 @@ export default function NavBar(): JSX.Element {
const signedInAs = () => { const signedInAs = () => {
if (user) { if (user) {
return ( return (
<p className="navbar-text"> <p className="text-secondary">
Signed in as <b>{user.full_name}</b> Signed in as <b>{user.full_name}</b>
</p> </p>
); );
@@ -23,9 +23,14 @@ export default function NavBar(): JSX.Element {
const customRules = () => { const customRules = () => {
if (user) { if (user) {
return ( return (
<li> <li className="mb-2">
<a target="_blank" href="https://wakatime.com/settings/rules" rel="noreferrer"> <a
<i className="fa fa-fw fa-filter"></i> target="_blank"
href="https://wakatime.com/settings/rules"
rel="noreferrer"
className="text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
>
<i className="fa fa-fw fa-filter me-2"></i>
Custom Rules Custom Rules
</a> </a>
</li> </li>
@@ -38,9 +43,14 @@ export default function NavBar(): JSX.Element {
const dashboard = () => { const dashboard = () => {
if (user) { if (user) {
return ( return (
<li> <li className="mb-2">
<a target="_blank" href="https://wakatime.com/dashboard" rel="noreferrer"> <a
<i className="fa fa-fw fa-tachometer"></i> target="_blank"
href="https://wakatime.com/dashboard"
rel="noreferrer"
className="text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
>
<i className="fa fa-fw fa-tachometer me-2"></i>
Dashboard Dashboard
</a> </a>
</li> </li>
@@ -51,65 +61,69 @@ export default function NavBar(): JSX.Element {
}; };
return ( return (
<nav className="navbar navbar-default" role="navigation"> <nav className="navbar shadow-none" role="navigation">
<div className="container-fluid"> <div className="navbar-header d-flex w-100 justify-content-between">
<div className="navbar-header"> <a target="_blank" className="navbar-brand" href="https://wakatime.com" rel="noreferrer">
<button <img src="graphics/wakatime-logo-48.png" />
type="button" <div>WakaTime</div>
className="navbar-toggle collapsed" </a>
data-toggle="collapse" <button
data-target="#bs-example-navbar-collapse-1" className="navbar-toggler"
> type="button"
<span className="sr-only">Toggle navigation</span> data-bs-toggle="collapse"
<i className="fa fa-fw fa-cogs"></i> data-bs-target="#userInfoCollapse"
</button> aria-controls="userInfoCollapse"
<a target="_blank" className="navbar-brand" href="https://wakatime.com" rel="noreferrer"> aria-expanded="false"
WakaTime aria-label="Toggle navigation"
<img src="graphics/wakatime-logo-48.png" /> >
</a> <span className="sr-only">Toggle navigation</span>
</div> <i className="fa fa-fw fa-cogs"></i>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> </button>
{signedInAs()} </div>
<ul className="nav navbar-nav">
{customRules()} <div className="collapse navbar-collapse mt-4" id="userInfoCollapse">
{dashboard()} {signedInAs()}
<li className="dropdown"> <ul className="nav navbar-nav border-bottom pb-2">
<a {customRules()}
href="#" {dashboard()}
className="dropdown-toggle" <li className="dropdown">
data-toggle="dropdown" <a
role="button" href="#"
aria-expanded="false" className="dropdown-toggle text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
> data-bs-toggle="dropdown"
<i className="fa fa-fw fa-info"></i> role="button"
About aria-expanded="false"
<span className="caret"></span> >
</a> <i className="fa fa-fw fa-info me-2"></i>
<ul className="dropdown-menu" role="menu"> About
<li> <span className="caret"></span>
<a </a>
target="_blank" <ul className="dropdown-menu shadow-none ms-4" role="menu">
href="https://github.com/wakatime/chrome-wakatime/issues" <li className="mb-2">
rel="noreferrer" <a
> target="_blank"
<i className="fa fa-fw fa-bug"></i> href="https://github.com/wakatime/chrome-wakatime/issues"
Report an Issue rel="noreferrer"
</a> className="text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
</li> >
<li> <i className="fa fa-fw fa-bug me-2"></i>
<a Report an Issue
target="_blank" </a>
href="https://github.com/wakatime/chrome-wakatime" </li>
rel="noreferrer" <li className="mb-2">
> <a
<i className="fa fa-fw fa-github"></i> target="_blank"
View on GitHub href="https://github.com/wakatime/chrome-wakatime"
</a> rel="noreferrer"
</li> className="text-body-secondary link-underline link-underline-opacity-0 d-flex w-100 align-items-center"
</ul> >
</li> <i className="fa fa-fw fa-github me-2"></i>
</ul> View on GitHub
</div> </a>
</li>
</ul>
</li>
</ul>
</div> </div>
</nav> </nav>
); );

View File

@@ -1,8 +1,9 @@
import { Toast } from 'bootstrap';
import classNames from 'classnames';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import config, { SuccessOrFailType } from '../config/config'; import config, { SuccessOrFailType } from '../config/config';
import apiKeyInvalid from '../utils/apiKey'; import apiKeyInvalid from '../utils/apiKey';
import { logUserIn } from '../utils/user'; import { logUserIn } from '../utils/user';
import Alert from './Alert';
import SitesList from './SitesList'; import SitesList from './SitesList';
interface State { interface State {
@@ -11,7 +12,6 @@ interface State {
apiKey: string; apiKey: string;
apiUrl: string; apiUrl: string;
blacklist: string; blacklist: string;
displayAlert: boolean;
hostname: string; hostname: string;
loading: boolean; loading: boolean;
loggingStyle: string; loggingStyle: string;
@@ -28,7 +28,6 @@ export default function Options(): JSX.Element {
apiKey: '', apiKey: '',
apiUrl: config.apiUrl, apiUrl: config.apiUrl,
blacklist: '', blacklist: '',
displayAlert: false,
hostname: '', hostname: '',
loading: false, loading: false,
loggingStyle: config.loggingStyle, loggingStyle: config.loggingStyle,
@@ -39,6 +38,8 @@ export default function Options(): JSX.Element {
whitelist: '', whitelist: '',
}); });
const liveToastRef = useRef(null);
const loggingStyleRef = useRef(null); const loggingStyleRef = useRef(null);
const restoreSettings = async (): Promise<void> => { const restoreSettings = async (): Promise<void> => {
@@ -82,14 +83,6 @@ export default function Options(): JSX.Element {
void restoreSettings(); void restoreSettings();
}, []); }, []);
useEffect(() => {
if (state.displayAlert) {
setTimeout(function () {
setState({ ...state, displayAlert: false, loading: false });
}, 2500);
}
}, [state.displayAlert]);
const handleSubmit = async () => { const handleSubmit = async () => {
if (state.loading) return; if (state.loading) return;
setState({ ...state, loading: true }); setState({ ...state, loading: true });
@@ -130,7 +123,6 @@ export default function Options(): JSX.Element {
apiKey, apiKey,
apiUrl, apiUrl,
blacklist, blacklist,
displayAlert: true,
hostname, hostname,
loggingStyle, loggingStyle,
loggingType, loggingType,
@@ -139,6 +131,8 @@ export default function Options(): JSX.Element {
trackSocialMedia, trackSocialMedia,
whitelist, whitelist,
}); });
// eslint-disable-next-line
Toast.getOrCreateInstance(liveToastRef?.current ?? '')?.show();
await logUserIn(state.apiKey); await logUserIn(state.apiKey);
}; };
@@ -183,20 +177,6 @@ export default function Options(): JSX.Element {
); );
}; };
const alert = () => {
return (
<div
style={{
height: state.displayAlert ? 55 : 0,
opacity: state.displayAlert ? 1 : 0,
transition: 'opacity 500ms, height 1000ms',
}}
>
<Alert key={state.alertText} type={state.alertType} text={state.alertText} />
</div>
);
};
const isApiKeyValid = apiKeyInvalid(state.apiKey) === ''; const isApiKeyValid = apiKeyInvalid(state.apiKey) === '';
return ( return (
@@ -211,114 +191,108 @@ export default function Options(): JSX.Element {
> >
<div className="row"> <div className="row">
<div className="col-md-12"> <div className="col-md-12">
{alert()}
<form className="form-horizontal"> <form className="form-horizontal">
<div className="form-group"> <div className="form-group mb-4">
<label className="col-lg-2 control-label">API Key</label> <label htmlFor="apiKey" className="form-label mb-0">
API Key
<div className="col-lg-10"> </label>
<input <input
autoFocus={true} id="apiKey"
type="text" autoFocus={true}
className={`form-control ${isApiKeyValid ? '' : 'border-danger'}`} type="text"
placeholder="API key" className={`form-control ${isApiKeyValid ? '' : 'is-invalid'}`}
value={state.apiKey} placeholder="API key"
onChange={(e) => setState({ ...state, apiKey: e.target.value })} value={state.apiKey}
/> onChange={(e) => setState({ ...state, apiKey: e.target.value })}
</div> />
</div> </div>
<div className="form-group"> <div className="form-group mb-4">
<label className="col-lg-2 control-label">Logging style!</label> <label htmlFor="loggingStyle" className="form-label">
Logging style
<div className="col-lg-10"> </label>
<select <select
ref={loggingStyleRef} id="loggingStyle"
className="form-control" ref={loggingStyleRef}
value={state.loggingStyle} className="form-control"
onChange={(e) => setState({ ...state, loggingStyle: e.target.value })} value={state.loggingStyle}
> onChange={(e) => setState({ ...state, loggingStyle: e.target.value })}
<option value="blacklist">All except blacklisted sites</option> >
<option value="whitelist">Only whitelisted sites</option> <option value="blacklist">All except blacklisted sites</option>
</select> <option value="whitelist">Only whitelisted sites</option>
</div> </select>
</div> </div>
{loggingStyle()} {loggingStyle()}
<div className="form-group"> <div className="form-group mb-4">
<label className="col-lg-2 control-label">Logging type</label> <label htmlFor="loggingType" className="form-label">
Logging type
<div className="col-lg-10"> </label>
<select <select
className="form-control" id="loggingType"
value={state.loggingType} className="form-control"
onChange={(e) => setState({ ...state, loggingType: e.target.value })} value={state.loggingType}
> onChange={(e) => setState({ ...state, loggingType: e.target.value })}
<option value="domain">Only the domain</option> >
<option value="url">Entire URL</option> <option value="domain">Only the domain</option>
</select> <option value="url">Entire URL</option>
</div> </select>
</div> </div>
<div className="form-group"> <div className="form-group mb-4">
<label htmlFor="theme" className="col-lg-2 control-label"> <label htmlFor="selectTheme" className="form-label mb-0">
Theme Theme
</label> </label>
<select
<div className="col-lg-10"> id="selectTheme"
<select className="form-control"
className="form-control" value={state.theme}
value={state.theme} onChange={(e) => setState({ ...state, theme: e.target.value })}
onChange={(e) => setState({ ...state, theme: e.target.value })} >
> <option value="light">Light</option>
<option value="light">Light</option> <option value="dark">Dark</option>
<option value="dark">Dark</option> </select>
</select>
</div>
</div> </div>
<div className="form-group"> <div className="form-group mb-4">
<label htmlFor="theme" className="col-lg-2 control-label"> <label htmlFor="selectHost" className="form-label mb-0">
Hostname Hostname
</label> </label>
<input
<div className="col-lg-10"> id="selectHost"
<input type="text"
type="text" className="form-control"
className="form-control" value={state.hostname}
value={state.hostname} onChange={(e) => setState({ ...state, hostname: e.target.value })}
onChange={(e) => setState({ ...state, hostname: e.target.value })} />
/> <span className="text-secondary">
<span className="help-block"> Optional name of local machine. By default &apos;Unknown Hostname&apos;.
Optional name of local machine. By default &apos;Unknown Hostname&apos;. </span>
</span>
</div>
</div> </div>
<div className="form-group"> <div className="form-group mb-4">
<label htmlFor="theme" className="col-lg-2 control-label"> <label htmlFor="apiUrl" className="form-label mb-0">
API Url API Url
</label> </label>
<div className="col-lg-10"> <input
<input id="apiUrl"
type="text" type="text"
className="form-control" className="form-control"
value={state.apiUrl} value={state.apiUrl}
onChange={(e) => setState({ ...state, apiUrl: e.target.value })} onChange={(e) => setState({ ...state, apiUrl: e.target.value })}
placeholder="https://api.wakatime.com/api/v1" placeholder="https://api.wakatime.com/api/v1"
/> />
<span className="help-block">https://api.wakatime.com/api/v1</span> <span className="help-block">https://api.wakatime.com/api/v1</span>
</div>
</div> </div>
<div className="form-group row"> <div className="form-group row mb-4">
<div className="col-lg-10 col-lg-offset-2 space-between align-items-center"> <div className="col-lg-10 col-lg-offset-2 space-between align-items-center">
<div> <div>
<input <input
type="checkbox" type="checkbox"
className="me-2"
checked={state.trackSocialMedia} checked={state.trackSocialMedia}
onChange={toggleSocialMedia} onChange={toggleSocialMedia}
/> />
@@ -327,8 +301,8 @@ export default function Options(): JSX.Element {
<button <button
type="button" type="button"
className="btn btn-primary btn-sm" className="btn btn-primary btn-sm"
data-toggle="modal" data-bs-toggle="modal"
data-target="#socialSitesModal" data-bs-target="#socialSitesModal"
> >
Sites Sites
</button> </button>
@@ -341,17 +315,15 @@ export default function Options(): JSX.Element {
<div className="modal-dialog" role="document"> <div className="modal-dialog" role="document">
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<button <h4 className="modal-title fs-5" id="socialSitesModalLabel">
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
<h4 className="modal-title" id="socialSitesModalLabel">
Social Media Sites Social Media Sites
</h4> </h4>
<button
type="button"
className="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<SitesList <SitesList
@@ -368,7 +340,7 @@ export default function Options(): JSX.Element {
/> />
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-primary" data-dismiss="modal"> <button type="button" className="btn btn-primary" data-bs-dismiss="modal">
Close Close
</button> </button>
</div> </div>
@@ -378,8 +350,8 @@ export default function Options(): JSX.Element {
</div> </div>
</div> </div>
<div className="form-group"> <div className="form-group mb-4">
<div className="col-lg-10 col-lg-offset-2"> <div className="d-grid gap-2 col-6 ">
<button <button
type="button" type="button"
className={`btn btn-primary ${state.loading ? 'disabled' : ''}`} className={`btn btn-primary ${state.loading ? 'disabled' : ''}`}
@@ -391,6 +363,30 @@ export default function Options(): JSX.Element {
</button> </button>
</div> </div>
</div> </div>
<div className="toast-container position-fixed bottom-0 end-0 p-3">
<div
className={classNames(
'toast align-items-center justify-content-between alert',
`alert-${state.alertType}`,
)}
role="alert"
aria-live="assertive"
aria-atomic="true"
id="liveToast"
ref={liveToastRef}
data-bs-delay="3000"
>
<div className="fs-5">{state.alertText}</div>
<div data-bs-theme="dark">
<button
type="button"
className="btn-close m-0"
data-bs-dismiss="toast"
aria-label="Close"
></button>
</div>
</div>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@@ -22,20 +22,21 @@ export default function SitesList({
}; };
return ( return (
<div className="form-group"> <div className="form-group mb-4">
<label htmlFor="sites" className="col-lg-2 control-label"> <label htmlFor={`${label}-siteList`} className="col-lg-2 control-label">
{label} {label}
</label> </label>
<div className="col-lg-10"> <div className="col-lg-10">
<textarea <textarea
id={`${label}-siteList`}
className="form-control" className="form-control"
rows={rows ?? 3} rows={rows ?? 3}
onChange={textareaChange} onChange={textareaChange}
placeholder={placeholder ?? 'http://google.com'} placeholder={placeholder ?? 'http://google.com'}
value={sites} value={sites}
></textarea> ></textarea>
<span className="help-block"> <span className="text-secondary">
{helpText} {helpText}
<br /> <br />
One line per site. One line per site.

View File

@@ -30,7 +30,7 @@ export default function WakaTime(): JSX.Element {
const isApiKeyValid = apiKeyInvalid(apiKeyFromRedux) === ''; const isApiKeyValid = apiKeyInvalid(apiKeyFromRedux) === '';
return ( return (
<div> <div className="py-4 px-2 pt-0">
<NavBar /> <NavBar />
{isApiKeyValid && extensionState === 'notSignedIn' && ( {isApiKeyValid && extensionState === 'notSignedIn' && (
<Alert <Alert
@@ -48,7 +48,7 @@ export default function WakaTime(): JSX.Element {
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
/> />
)} )}
<div className="container"> <div className="container mt-0">
<div className="row"> <div className="row">
<div className="col-md-12"> <div className="col-md-12">
<MainList loggingEnabled={loggingEnabled} totalTimeLoggedToday={totalTimeLoggedToday} /> <MainList loggingEnabled={loggingEnabled} totalTimeLoggedToday={totalTimeLoggedToday} />

View File

@@ -5,9 +5,7 @@ import WakaTime from './components/WakaTime';
import createStore from './stores/createStore'; import createStore from './stores/createStore';
import checkCurrentUser from './utils/checkCurrentUser'; import checkCurrentUser from './utils/checkCurrentUser';
/* This is a fix for Bootstrap requiring jQuery */ import 'bootstrap/dist/js/bootstrap';
global.jQuery = require('jquery');
require('bootstrap');
const container = document.getElementById('wakatime'); const container = document.getElementById('wakatime');
const root = createRoot(container!); const root = createRoot(container!);

View File

@@ -82,11 +82,11 @@ load({
'clean:webpack': exec('rimraf dist'), 'clean:webpack': exec('rimraf dist'),
dev: ['clean', 'postinstall', concurrent('watch', 'web-ext:run:firefox', 'web-ext:run:chrome')], dev: ['clean', 'postinstall', concurrent('watch', 'web-ext:run:firefox', 'web-ext:run:chrome')],
eslint: exec('eslint src . --fix'), eslint: exec('eslint src . --fix'),
less: exec('lessc assets/less/app.less public/css/app.css'),
lint: ['prettier', 'eslint'], lint: ['prettier', 'eslint'],
postinstall: ['clean', makePublicFolder, copyFromNodeModules, 'less'], postinstall: ['clean', makePublicFolder, copyFromNodeModules, 'sass'],
prettier: [exec('prettier --write .')], prettier: [exec('prettier --write .')],
'remotedev-server': exec('remotedev --hostname=localhost --port=8000'), 'remotedev-server': exec('remotedev --hostname=localhost --port=8000'),
sass: exec('node-sass assets/sass/app.scss public/css/app.css'),
test: ['build', 'lint', 'test-jest'], test: ['build', 'lint', 'test-jest'],
'test-jest': [exec('jest --clearCache'), exec('jest --verbose --coverage')], 'test-jest': [exec('jest --clearCache'), exec('jest --verbose --coverage')],
watch: concurrent('watch-jest', 'webpack:watch'), watch: concurrent('watch-jest', 'webpack:watch'),