avancé de dev

This commit is contained in:
Ambulance Clerc
2022-02-15 14:48:18 +01:00
parent d84d7d9823
commit c990f87413
162 changed files with 27899 additions and 27 deletions

View File

@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

View File

@@ -0,0 +1,2 @@
node_modules/**
dist/**

View File

@@ -0,0 +1,24 @@
{
"parser" : "babel-eslint",
"extends" : [
"standard"
],
"plugins" : [
"flow-vars"
],
"env" : {
"browser" : true
},
"globals" : {
"Baton": {},
"jQuery": {},
"$": {},
},
"rules": {
"semi" : [2, "never"],
"max-len": [2, 120, 2],
"comma-dangle": [2, "only-multiline"],
"flow-vars/define-flow-type": 1,
"flow-vars/use-flow-type": 1
}
}

View File

@@ -0,0 +1,13 @@
{
"libs": [
"browser",
"ecma5",
"ecma6"
],
"plugins": {
"node": { },
"modules": { },
"complete_strings": {},
"es_modules": {}
}
}

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 898 KiB

View File

@@ -0,0 +1,3 @@
<svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path fill="#555555" d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"/>
</svg>

After

Width:  |  Height:  |  Size: 458 B

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 730 KiB

View File

@@ -0,0 +1,34 @@
<svg width="16" height="192" viewBox="0 0 1792 21504" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="up">
<path d="M1412 895q0-27-18-45l-362-362-91-91q-18-18-45-18t-45 18l-91 91-362 362q-18 18-18 45t18 45l91 91q18 18 45 18t45-18l189-189v502q0 26 19 45t45 19h128q26 0 45-19t19-45v-502l189 189q19 19 45 19t45-19l91-91q18-18 18-45zm252 1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="down">
<path d="M1412 897q0-27-18-45l-91-91q-18-18-45-18t-45 18l-189 189v-502q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v502l-189-189q-19-19-45-19t-45 19l-91 91q-18 18-18 45t18 45l362 362 91 91q18 18 45 18t45-18l91-91 362-362q18-18 18-45zm252-1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="left">
<path d="M1408 960v-128q0-26-19-45t-45-19h-502l189-189q19-19 19-45t-19-45l-91-91q-18-18-45-18t-45 18l-362 362-91 91q-18 18-18 45t18 45l91 91 362 362q18 18 45 18t45-18l91-91q18-18 18-45t-18-45l-189-189h502q26 0 45-19t19-45zm256-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="right">
<path d="M1413 896q0-27-18-45l-91-91-362-362q-18-18-45-18t-45 18l-91 91q-18 18-18 45t18 45l189 189h-502q-26 0-45 19t-19 45v128q0 26 19 45t45 19h502l-189 189q-19 19-19 45t19 45l91 91q18 18 45 18t45-18l362-362 91-91q18-18 18-45zm251 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="clearall">
<path transform="translate(336, 336) scale(0.75)" d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="chooseall">
<path transform="translate(336, 336) scale(0.75)" d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
</defs>
<use xlink:href="#up" x="0" y="0" fill="#666666" />
<use xlink:href="#up" x="0" y="1792" fill="#447e9b" />
<use xlink:href="#down" x="0" y="3584" fill="#666666" />
<use xlink:href="#down" x="0" y="5376" fill="#447e9b" />
<use xlink:href="#left" x="0" y="7168" fill="#666666" />
<use xlink:href="#left" x="0" y="8960" fill="#447e9b" />
<use xlink:href="#right" x="0" y="10752" fill="#666666" />
<use xlink:href="#right" x="0" y="12544" fill="#447e9b" />
<use xlink:href="#clearall" x="0" y="14336" fill="#666666" />
<use xlink:href="#clearall" x="0" y="16128" fill="#447e9b" />
<use xlink:href="#chooseall" x="0" y="17920" fill="#666666" />
<use xlink:href="#chooseall" x="0" y="19712" fill="#447e9b" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,19 @@
<svg width="14" height="84" viewBox="0 0 1792 10752" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="sort">
<path d="M1408 1088q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45zm0-384q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"/>
</g>
<g id="ascending">
<path d="M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"/>
</g>
<g id="descending">
<path d="M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"/>
</g>
</defs>
<use xlink:href="#sort" x="0" y="0" fill="#999999" />
<use xlink:href="#sort" x="0" y="1792" fill="#447e9b" />
<use xlink:href="#ascending" x="0" y="3584" fill="#999999" />
<use xlink:href="#ascending" x="0" y="5376" fill="#447e9b" />
<use xlink:href="#descending" x="0" y="7168" fill="#999999" />
<use xlink:href="#descending" x="0" y="8960" fill="#447e9b" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,14 @@
<svg width="15" height="60" viewBox="0 0 1792 7168" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="previous">
<path d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="next">
<path d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
</defs>
<use xlink:href="#previous" x="0" y="0" fill="#333333" />
<use xlink:href="#previous" x="0" y="1792" fill="#000000" />
<use xlink:href="#next" x="0" y="3584" fill="#333333" />
<use xlink:href="#next" x="0" y="5376" fill="#000000" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
/*!
* Bootstrap v5.0.1 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
/*!
* Sizzle CSS Selector Engine v2.3.6
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
* Date: 2021-02-16
*/
/*!
* jQuery JavaScript Library v3.6.0
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2021-03-02T17:08Z
*/

View File

@@ -0,0 +1,3 @@
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path fill="#666666" d="M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 655 B

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
{
"name": "baton",
"version": "2.2.3",
"description": "Django Baton App",
"main": "index.js",
"scripts": {
"stats": "npx webpack --config ./webpack.prod.js --json > stats.json",
"compile": "BATON_REVISION=$(git rev-parse HEAD) npx webpack --config ./webpack.prod.js",
"watch": "npx webpack --watch --config ./webpack.dev.js",
"dev": "BATON_REVISION=$(git rev-parse HEAD) webpack serve --host 0.0.0.0 --progress --config ./webpack.dev.js",
"lint": "./node_modules/eslint/bin/eslint.js ."
},
"author": "abidibo",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"@babel/register": "^7.13.16",
"@fortawesome/fontawesome-free": "^5.15.3",
"autoprefixer": "^10.2.6",
"babel-cli": "^6.26.0",
"babel-loader": "^8.2.2",
"babel-register": "^6.26.0",
"bootstrap": "^5.0.1",
"css-loader": "^5.2.6",
"file-loader": "^6.2.0",
"ini": "^2.0.0",
"jquery": "^3.6.0",
"js-event-dispatcher": "^0.1.0",
"loader": "^2.1.1",
"lodash": "^4.17.21",
"mini-svg-data-uri": "^1.3.3",
"node-sass": "^6.0.0",
"postcss-loader": "^5.3.0",
"sass": "^1.34.0",
"sass-loader": "^11.1.1",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.38.1",
"webpack-dev-server": "^3.11.2"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint": "^7.27.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-flow-vars": "^0.5.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^5.0.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.0",
"webpack-merge": "^5.7.3"
}
}

View File

@@ -0,0 +1,5 @@
module.exports = {
plugins: [
require('autoprefixer')
]
}

View File

@@ -0,0 +1,6 @@
set smarttab
set shiftwidth=2
set softtabstop=2
set tabstop=2
set expandtab
let syntastic_scss_checkers = ['scss_lint']

View File

@@ -0,0 +1,14 @@
import $ from 'jquery'
let ActionResult = {
/**
* Menu component
*
* Adds a sidebar menu to the document
*/
init: function () {
$('body').addClass('actionresult')
}
}
export default ActionResult

View File

@@ -0,0 +1,15 @@
import $ from 'jquery'
let AdminDocs = {
/**
* Footer component
*
* Moves the footer inside the main external container
*/
init: function (opts) {
let container = $('<div />', {'class': 'admindocs-body'})
container.append($('#content > *:not(h1):not(.breadcrumbs)')).appendTo($('#content'))
}
}
export default AdminDocs

View File

@@ -0,0 +1,214 @@
import Translator from './i18n'
class Analytics {
constructor (gapi, token, viewId, domIds) {
this.gapi = gapi
this.token = token
this.domIds = domIds
this.viewId = viewId
this.t = new Translator($('html').attr('lang'))
}
init (timedelta) {
let self = this
this.gapi.analytics.ready(function () { self.run(timedelta) })
}
run (timedelta) {
let self = this
let gapi = this.gapi
let spinner = $('<div />').css({
textAlign: 'center',
padding: '3rem 0',
color: '#aaa',
}).append($('<i />', {'class': 'fa fa-spinner fa-spin fa-3x fa-fw'}))
for (let prop in this.domIds) {
if (['traffic', 'popular', 'browsers', 'acquisition', 'audience', 'social'].indexOf(prop) !== -1) {
$('#' + this.domIds[prop]).append(spinner.clone())
}
}
let errorCb = containerId => () => {
$('#' + containerId).empty()
let message = $('<div />').css({
textAlign: 'center',
padding: '3rem 0',
color: '#aaa',
}).text(this.t.get('retrieveDataError'))
$('#' + containerId).append(message)
}
gapi.analytics.ready(function () {
/**
* Authorize the user with an access token obtained server side.
*/
gapi.analytics.auth.authorize({
'serverAuth': {
'access_token': self.token
}
})
/**
* Create a new ViewSelector2 instance to be rendered inside of an
* element with the id "view-selector-container".
*/
var viewSelector = new gapi.analytics.ViewSelector({
container: self.domIds.viewSelector,
})
viewSelector.execute()
let baseQuery = {
'ids': 'ga:' + self.viewId,
'start-date': timedelta,
'end-date': 'yesterday'
}
/**
* Creates a new DataChart instance showing sessions over the past 15 days.
*/
let dataChart1 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions,ga:users',
'dimensions': 'ga:date'
},
chart: {
'container': self.domIds.traffic,
'type': 'LINE',
'options': {
'width': '100%'
}
}
})
let trafficTimeout = setTimeout(errorCb(self.domIds.traffic), 20000)
dataChart1.on('error', errorCb(self.domIds.traffic))
dataChart1.on('success', () => clearTimeout(trafficTimeout))
dataChart1.execute()
/**
* Creates a new DataChart instance showing top 5 most popular pages
*/
var dataChart2 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:pageviews',
'dimensions': 'ga:pagePath',
'sort': '-ga:pageviews',
'max-results': 7
},
chart: {
'container': self.domIds.popular,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let popularTimeout = setTimeout(errorCb(self.domIds.popular), 20000)
dataChart2.on('error', errorCb(self.domIds.popular))
dataChart2.on('success', () => clearTimeout(popularTimeout))
dataChart2.execute()
/**
* Creates a new DataChart instance showing top borwsers
*/
var dataChart3 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions',
'dimensions': 'ga:browser',
'sort': '-ga:sessions',
'max-results': 7
},
chart: {
'container': self.domIds.browsers,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let browsersTimeout = setTimeout(errorCb(self.domIds.browsers), 20000)
dataChart3.on('error', errorCb(self.domIds.browsers))
dataChart3.on('success', () => clearTimeout(browsersTimeout))
dataChart3.execute()
/**
* Creates a new DataChart instance showing top referral
*/
var dataChart4 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions',
'dimensions': 'ga:source',
'sort': '-ga:sessions',
'max-results': 7
},
chart: {
'container': self.domIds.acquisition,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let acquisitionTimeout = setTimeout(errorCb(self.domIds.acquisition), 20000)
dataChart4.on('error', errorCb(self.domIds.acquisition))
dataChart4.on('success', () => clearTimeout(acquisitionTimeout))
dataChart4.execute()
/**
* Creates a new DataChart instance showing top visitors continents
*/
var dataChart5 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions',
'dimensions': 'ga:country',
'sort': '-ga:sessions',
'max-results': 7
},
chart: {
'container': self.domIds.audience,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let audienceTimeout = setTimeout(errorCb(self.domIds.audience), 20000)
dataChart5.on('error', errorCb(self.domIds.audience))
dataChart5.on('success', () => clearTimeout(audienceTimeout))
dataChart5.execute()
/**
* Creates a new DataChart instance showing social interactions over the past 15 days.
*/
var dataChart6 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:socialInteractions',
'dimensions': 'ga:socialInteractionNetwork',
'sort': '-ga:socialInteractions',
'max-results': 7
},
chart: {
'container': self.domIds.social,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let socialTimeout = setTimeout(errorCb(self.domIds.social), 20000)
dataChart6.on('error', errorCb(self.domIds.social))
dataChart6.on('success', () => clearTimeout(socialTimeout))
dataChart6.execute()
})
}
}
export default Analytics

View File

@@ -0,0 +1,7 @@
export default {
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400
}

View File

@@ -0,0 +1,193 @@
import $ from 'jquery'
import Translator from './i18n'
let ChangeForm = {
/**
* ChangeForm component
*
* Alert unsaved changes stuff
* Display loading spinner if multipart
*/
init: function (opts) {
var self = this
this.form = $('#content-main form')
if (opts.confirmUnsavedChanges) {
this.formSubmitting = false
this.t = new Translator($('html').attr('lang'))
// wait for django SelectFilter to do its job
setTimeout(function () {
self.initData = self.serializeData()
self.activate()
}, 500)
}
if (opts.showMultipartUploading) {
this.spinner()
}
self.fixNewlines()
setTimeout(function () {
self.fixNewlines() // js inserted ones
}, 50)
this.fixWrappedFields()
if (opts.enableImagesPreview) {
this.lazyLoadImages()
}
this.activateEntryCollapsing()
this.changeFieldsetCollapseStyle()
this.fixExpandFirstErrorCollapsing()
this.initTemplates()
},
activate: function () {
this.form.on('submit', () => (this.formSubmitting = true))
$(window).on('beforeunload', this.alertDirty.bind(this))
},
serializeData: function () {
// form serialize does not detect filter_horizontal controllers because
// in that case options are not selected, just added to the list of options,
// and jquery form serialize only serializes values which are set!
let data = this.form.serialize()
$('select.filtered[multiple][id$=_to]').each(function (k, select) {
let optionsValues = []
$(select).children('option').each(function (kk, option) {
optionsValues.push($(option).attr('value'))
})
data += `&${jQuery(select).attr('name')}=${optionsValues.sort().join(',')}`
})
return data
},
isDirty: function () {
return this.serializeData() !== this.initData
},
alertDirty: function (e) {
if (this.formSubmitting || !this.isDirty()) {
return undefined
}
let confirmationMessage = this.t.get('unsavedChangesAlert');
(e || window.event).returnValue = confirmationMessage // Gecko + IE
return confirmationMessage // Gecko + Webkit, Safari, Chrome etc.
},
spinner: function () {
if (this.form.attr('enctype') === 'multipart/form-data') {
this.form.on('submit', () => this.showSpinner())
}
},
showSpinner: function () {
let run = false
let inputFiles = $('input[type=file]')
inputFiles.each(function (index, input) {
if (input.files.length !== 0) {
run = true
}
})
if (run) {
let overlay = $('<div />', {'class': 'spinner-overlay'}).appendTo(document.body)
let spinner = $('<i />', {'class': 'fa fa-spinner fa-spin fa-3x fa-fw'})
$('<div />').append(
$('<p />').text(this.t.get('uploading')),
spinner
).appendTo(overlay)
}
},
fixWrappedFields: function () {
this.form.find('.form-row').each(function (index, row) {
var fieldBoxes = $(row).children('.fieldBox')
fieldBoxes.each(function (index, fbox) {
if ($(fbox).hasClass('errors')) {
$(row).addClass('errors')
}
})
fieldBoxes.wrapAll('<div class="wrapped-fields-container" />')
if (fieldBoxes.length) {
$(row).addClass('with-wrapped-fields')
}
})
// this.form.find('.wrapped-fields-container > .fieldBox:first-child').children().unwrap()
},
fixNewlines: function () {
$('.form-row br').replaceWith('<span class="newline"></span>')
},
lazyLoadImages: function () {
$('.file-upload').each(function (index, p) {
let cur = $(p).find('a')
if (cur.length) {
let url = cur.attr('href')
let ext = url.split('?')[0].split('.').pop()
if (['jpg', 'jpeg', 'png', 'bmp', 'svg', 'gif', 'tif', 'webp'].indexOf(ext) !== -1) {
let spinner = $('<i />', {'class': 'fa fa-spinner fa-spin fa-2x fa-fw'}).css('color', '#aaa')
let preview = $('<div />', {'class': 'py-2'}).append(spinner)
$(p).prepend(preview)
let image = new Image()
image.onload = function () {
spinner.replaceWith($(image).addClass('baton-image-preview'))
}
image.onerror = function () {
preview.remove()
}
image.src = url
}
}
})
},
activateEntryCollapsing: function () {
$('.collapse-entry h3')
.addClass('entry-collapsed entry-collapse-full-toggler')
.append('<span />') // just to have the toggler right aligned
.append('<span class="entry-collapse-toggler" />')
$('.collapse-entry')
.click(function (e) {
let target = $(e.target)
if (target.hasClass('entry-collapse-full-toggler')) {
target.toggleClass('entry-collapsed')
} else if (target.parent('.entry-collapse-full-toggler').length > 0) {
target.parent('.entry-collapse-full-toggler').toggleClass('entry-collapsed')
}
})
$('.form-row.errors').each(function (index, el) {
if ($(el).parent('fieldset').prev('h3.entry-collapsed')) {
$(el).parent('fieldset').prev('h3.entry-collapsed').removeClass('entry-collapsed')
}
})
},
fixExpandFirstErrorCollapsing: function () {
$('.expand-first').each(function (index, el) {
if ($(el).find('.inline-related[id$=0] .form-row.errors').length) {
// inverse logic
$(el).find('.inline-related[id$=0] .form-row.errors').parent('fieldset').prev('h3').addClass('entry-collapsed')
}
})
},
changeFieldsetCollapseStyle: function () {
$(window).on('load', function () {
$('fieldset.collapse > h2').each(function (index, title) {
let text = $(title).text().replace(/\(.*\)/, '')
setTimeout(function () {
$(title).html(text).on('click', function () {
$(this).parent('.collapse').toggleClass('collapsed')
})
}, 100)
})
})
},
initTemplates: function () {
const positionMap = {
above: 'before',
below: 'after',
top: 'prepend',
bottom: 'append',
right: 'after'
}
$('template').each(function (index, template) {
let field = $(template).attr('id').replace('template-', '')
let position = positionMap[$(template).attr('data-position')]
if (position !== undefined) {
let el = $(template).attr('data-position') === 'right'
? $('.form-row.field-' + field + ' #id_' + field)
: $('.form-row.field-' + field)
el[position]($(template).html())
} else {
console.error('Baton: wrong form include position detected')
}
})
}
}
export default ChangeForm

View File

@@ -0,0 +1,203 @@
import $ from 'jquery'
import Translator from './i18n'
import Modal from './Modal'
import breakpoints from './Breakpoints'
let ChangeList = {
/**
* ChangeList component
*
* Filtering stuff
*/
init: function (opts) {
this._filtersDiv = $('#changelist-filter')
this.t = new Translator($('html').attr('lang'))
this.filtersForm = opts.changelistFiltersForm
this.filtersInModal = opts.changelistFiltersInModal
this.filtersAlwaysOpen = opts.changelistFiltersAlwaysOpen
this.initTemplates()
if (this._filtersDiv.length) {
var self = this
setTimeout(function () {
self.activate()
}, 200) // select2
this.fixRangeFilter()
}
},
activate: function () {
if ($('.changelist-form-container').length) {
// django >= 3.1
$('#changelist-filter').appendTo($('.changelist-form-container'))
}
let isModal = false
if (this.filtersAlwaysOpen) {
$(document.body).addClass(
'changelist-filter-active changelist-filter-always-open'
)
} else {
// filters active?
let _activeFilters = /__[^=]+=/.test(location.search)
// actions ?
let _activeActions = $('#changelist-form > .actions').length !== 0
let _changelistForm = $('#changelist-form')
let _filtersToggler = $('<a />', {
class:
'changelist-filter-toggler' +
(_activeFilters ? ' active' : '') +
(_activeActions ? ' with-actions' : '')
}).html(
'<i class="fa fa-filter"></i> <span>' + this.t.get('filter') + '</span>'
)
if (this.filtersInModal || parseInt($(window).width()) < breakpoints.lg) {
let self = this
isModal = true
// wait for filters used js to exec
$('#changelist-filter').prop('id', 'changelist-filter-modal')
let titleEl = $('#changelist-filter-modal > h2')
let title = titleEl.html()
titleEl.remove()
let content = $('#changelist-filter-modal')
// remove from dom
this.modal = new Modal({
title,
content,
size: 'md',
hideFooter: !this.filtersForm,
actionBtnLabel: this.t.get('filter'),
actionBtnCb: function () { self.filter(content) }
})
_filtersToggler.click(() => {
self.modal.toggle()
})
} else {
_filtersToggler.click(() => {
$(document.body).toggleClass('changelist-filter-active')
if (parseInt(this._filtersDiv.css('max-width')) === 100) {
// diff between mobile and lg
$('html,body').animate({
scrollTop: this._filtersDiv.offset().top
})
}
})
}
_changelistForm.prepend(_filtersToggler)
}
if (!isModal && this.filtersForm) {
// add filters button
let btn = $('<a />', {'class': 'btn btn-primary'}).html(this.t.get('filter'))
.on('click', () => this.filter($('#changelist-filter')))
$('#changelist-filter').append($('<div />', {'class': 'text-center mb-3'}).append(btn))
}
if (/_popup=1/.test(location.href)) {
$('#changelist-form .results').css('padding-top', '78px')
}
},
getDropdownValue: function (dropdown) {
let items = $(dropdown).find('option').attr('value').substr(1).split('&')
let values = $(dropdown).val().substr(1).split('&').filter(item => items.indexOf(item) === -1)
return values.length ? values.join('&') : null
},
filter: function (wrapper) {
var self = this
let qs = []
let dropdowns = wrapper.find('select')
let textInputs = wrapper.find('input').not('[type=hidden]')
dropdowns
.toArray()
.map(el => self.getDropdownValue(el))
.filter(v => v !== null)
.forEach(v => qs.push(v))
textInputs.each((idx, el) => el.value !== '' ? qs.push(`${el.name}=${el.value}`) : null)
// console.log(location.pathname + (qs.length ? '?' + qs.filter(q => q !== '').join('&') : ''), qs)
location.href = location.pathname + (qs.length ? '?' + qs.filter(q => q !== '').join('&') : '')
},
initTemplates: function () {
const positionMap = {
above: 'before',
below: 'after',
top: 'prepend',
bottom: 'append'
}
$('template[data-type=include]').each(function (index, template) {
let position = positionMap[$(template).attr('data-position')]
if (position !== undefined) {
let el = $('#changelist-form')
el[position]($(template).html())
} else {
console.error('Baton: wrong changelist include position detected')
}
})
$('template[data-type=filters-include]').each(function (index, template) {
let position = positionMap[$(template).attr('data-position')]
if (
position !== undefined &&
position !== 'before' &&
position !== 'after'
) {
if (position === 'prepend' && $('#changelist-filter-clear').length) {
$('#changelist-filter-clear').after($(template).html())
} else if (
position === 'prepend' &&
$('#changelist-filter > h2').length
) {
$('#changelist-filter > h2').after($(template).html())
} else {
let el = $('#changelist-filter')
el[position]($(template).html())
}
} else {
console.error(
'Baton: wrong changelist filters include position detected'
)
}
})
$('template[data-type=attributes]').each(function (index, template) {
try {
let data = JSON.parse($(template).html())
for (let key in data) {
if (data.hasOwnProperty(key)) {
let selector
let getParent = 'tr'
if (data[key]['selector']) {
selector = data[key]['selector']
delete data[key]['selector']
} else {
selector =
'#result_list tr input[name=_selected_action][value=' +
key +
']'
}
if (data[key]['getParent'] !== undefined) {
getParent = data[key]['getParent']
delete data[key]['getParent']
}
let el = getParent ? $(selector).parents(getParent) : $(selector)
el.attr(data[key])
}
}
} catch (e) {
console.error(e)
}
})
},
fixRangeFilter: function () {
if (this.filtersForm) {
$('.admindatefilter .controls').remove()
$('.admindatefilter form').onSubmit = function () { return false }
}
}
}
export default ChangeList

View File

@@ -0,0 +1,30 @@
import $ from 'jquery'
import { copyTextToClipboard } from './Utils'
let Filer = {
/**
* ChangeList component
*
* Filtering stuff
*/
init: function (opts) {
this.fixIcons()
this.fixCopyToClipboard()
},
fixIcons: function () {
$('.fa-pencil').addClass('fa-pencil-alt')
},
fixCopyToClipboard: function () {
let copyBtns = $('.action-button .fa-link')
copyBtns.on('click', function (evt) {
evt.preventDefault()
var link = $(this).parent('.action-button').attr('href')
if (!link) {
link = $(this).parent('.action-button').next('.action-button').attr('href')
}
copyTextToClipboard(link)
})
}
}
export default Filer

View File

@@ -0,0 +1,17 @@
import $ from 'jquery'
let Footer = {
/**
* Footer component
*
* Moves the footer inside the main external container
*/
init: function (opts) {
$('#footer').appendTo('#content')
if (opts.remove) {
$('#footer').remove()
}
}
}
export default Footer

View File

@@ -0,0 +1,47 @@
import $ from 'jquery'
const Login = {
init: function (config) {
// splash
if (config.loginSplash) {
$('body.login').css({
background: `url(${config.loginSplash}) no-repeat center center`,
backgroundSize: 'cover'
})
}
// form
let inputUsername = $('#id_username')
let inputPassword = $('#id_password')
const usernameField = $('<div />', { class: 'input-group mb-2' })
.append(
$('<span />', { class: 'input-group-text' }).append(
'<i class="fa fa-user"></i>'
)
)
.append(inputUsername.clone())
inputUsername.replaceWith(usernameField)
// adds show/hide password functionality
let passwordInputField = inputPassword.clone()
let viewPasswordIcon = $('<i />', {'class': 'fa fa-eye pwd-visibility-toggle'}).on('click', function () {
let visible = $(this).hasClass('fa-eye-slash')
$(this)[visible ? 'removeClass' : 'addClass']('fa-eye-slash')
passwordInputField.attr('type', visible ? 'password' : 'text')
})
const passwordField = $('<div />', { class: 'input-group mb-2' })
.append(
$('<span />', { class: 'input-group-text' }).append(
'<i class="fa fa-key"></i>'
)
)
.append(passwordInputField)
.append(viewPasswordIcon)
inputPassword.replaceWith(passwordField)
}
}
export default Login

View File

@@ -0,0 +1,375 @@
import $ from 'jquery'
import Translator from './i18n'
let Menu = {
/**
* Menu component
*
* Adds a sidebar menu to the document
*/
init: function (config, Dispatcher) {
this.Dispatcher = Dispatcher
this.t = new Translator($('html').attr('lang'))
this.collapsableUserArea = config.collapsableUserArea
this.menuTitle = config.menuTitle
this.searchField = config.searchField
this.appListUrl = config.api.app_list
this.gravatarUrl = config.api.gravatar
this.gravatarDefaultImg = config.gravatarDefaultImg
this.alwaysCollapsed = $('#header').hasClass('menu-always-collapsed')
this.fixNodes()
this.brandingClone = $('#branding').clone()
this.manageBrandingUserTools()
this.manageSearchField()
this.fetchData()
this.setHeight()
let self = this
$(window).on('resize', function () {
self.setHeight()
self.manageBrandingUserTools()
})
},
fixNodes: function () {
let container = $('<div/>', { class: 'container-fluid' })
$('#footer').before(container)
let row = $('<div/>', { class: 'row' }).appendTo(container)
this.menu = $('<nav/>', { class: 'col-lg-2 sidebar-menu' }).appendTo(row)
$('#content')
.addClass('col-lg-10')
.prepend($('.breadcrumbs'))
.appendTo(row)
$('#content > h1').after($('.messagelist'))
let title = $('<h1 />', { class: 'd-block d-lg-none' }).text(
this.menuTitle ? this.menuTitle : 'Menu'
)
$('<i/>', { class: 'fa fa-times' })
.click(() => {
$(document.body).removeClass('menu-open')
})
.appendTo(title)
this.menu.append(title)
if (this.alwaysCollapsed) {
let close = $('<i />', { class: 'fa fa-times toggle-menu' })
.appendTo(this.menu)
.click(() => {
$(document.body).removeClass('menu-open')
})
}
},
manageBrandingUserTools: function () {
if (parseInt($(window).width()) >= 992) {
// move user tools
this.menu.prepend($('#user-tools'))
if (this.alwaysCollapsed) {
// copy branding
this.menu.prepend(this.brandingClone)
} else {
// move branding
this.menu.prepend($('#branding'))
}
if ($('#user-tools-sidebar').length === 0) {
this.renderUserTools()
}
} else {
$('#header').append($('#user-tools'))
if (this.alwaysCollapsed) {
this.menu.find('#branding').remove()
} else {
$('#header .navbar-toggler').after($('#branding'))
}
if ($('#user-tools-sidebar').length === 0) {
this.removeUserTools()
}
}
},
manageSearchField () {
// unset
if (!this.searchField || !this.searchField.url) {
return
}
let container = $('<div />', { class: 'search-field-tool' })
let field = $('<input />', {
class: 'form-control form-control-sm',
type: 'text',
list: 'admin-search-datalist',
placeholder: this.searchField.label || this.t('search')
})
let dataList = $('<div />', { id: 'admin-search-datalist' }).on('mouseover', e => {
if ($(e.target).hasClass('datalist-option') || $(e.target).parent('.datalist-option').length) {
dataList.find('.datalist-option').removeClass('selected')
let item = $(e.target).hasClass('datalist-option') ? $(e.target) : $(e.target).parent('.datalist-option')
item.addClass('selected')
}
})
let navigateDataList = code => {
let target
let active = dataList.find('.datalist-option.selected').first()
if (!active.length) {
target = dataList.find('.datalist-option')[code === 40 ? 'first' : 'last']()
} else {
if (code === 40) {
let next = active.next()
target = next.length ? next : dataList.find('.datalist-option').first()
} else {
let prev = active.prev()
target = prev.length ? prev : dataList.find('.datalist-option').last()
}
}
if (target) {
active.removeClass('selected')
$(target).addClass('selected')
target[0].scrollIntoView({
behavior: 'smooth',
block: 'end',
inline: 'nearest'
})
}
}
field.on('blur', e => setTimeout(() => dataList.hide(), 150))
field.on('focus', e => dataList.show())
field.on('keyup', e => {
var code = e.keyCode || e.which
if (code === 13) {
// goto url if there is an active voice
let active = dataList.find('.datalist-option.selected').first()
if (active.length) {
location.href = active.attr('data-url')
}
return
}
if ([40, 38].indexOf(code) !== -1) {
// move
navigateDataList(code)
} else {
// search
if ($(field).val().length < 1) {
dataList.empty()
return
}
container.addClass('loading')
$.getJSON(this.searchField.url, { text: $(field).val() })
.done(data => {
container.removeClass('loading')
dataList.empty()
data.data.forEach((r, index) => dataList.append(`
<div class="datalist-option${index === 0 ? ' selected' : ''}" onclick="location.href='${r.url}'" data-url="${r.url}"><a href="${r.url}">${r.label}</a>${r.icon ? `<i onclick="location.href='${r.url}'" class="${r.icon}"></i>` : ''}</div>`)
)
})
.fail((jqxhr, textStatus, err) => {
console.log(err)
container.removeClass('loading')
dataList.empty()
})
}
})
$('#user-tools-sidebar').after(container.append([field, dataList]))
},
renderUserTools: function () {
let self = this
let container = $('<div />', { id: 'user-tools-sidebar' })
let expandUserArea = $('<i />', {'class': 'fa fa-angle-down user-area-toggler'}).on('click', function () {
$(this).toggleClass('fa-angle-up')
container.toggleClass('collapsed')
})
if (this.collapsableUserArea) {
container.addClass('collapsed')
}
container.insertAfter('#user-tools')
let userInfo = $('<div />', { class: 'user-info' })
.html(
'<div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div><div>' +
$('#user-tools .dropdown-toggle').text() +
'</div>'
)
.appendTo(container)
// gravatar
$.getJSON(this.gravatarUrl, function (data) {
let img = $('<img />', {
class: 'gravatar-icon',
src: 'https://www.gravatar.com/avatar/{hash}?s=50&d={default}'
.replace('{hash}', data.hash)
.replace('{default}', self.gravatarDefaultImg)
})
userInfo.find('.spinner-border').replaceWith(img)
if (self.collapsableUserArea) {
img.after(expandUserArea)
}
}).fail(function (err) {
console.error(err.responseText)
let img = $('<img />', {
class: 'gravatar-icon',
src: 'https://www.gravatar.com/avatar/{hash}?s=50&d={default}'
.replace('{hash}', '')
.replace('{default}', self.gravatarDefaultImg)
})
userInfo.find('.spinner-border').replaceWith(img)
if (self.collapsableUserArea) {
img.after(expandUserArea)
}
})
let linksContainer = $('<div />', { class: 'user-links' }).appendTo(
container
)
$('#user-tools .dropdown-menu a').each(function (index, el) {
let cls = 'view-site'
if (/password_change/.test($(el).attr('href'))) {
cls = 'password'
} else if (/logout/.test($(el).attr('href'))) {
cls = 'logout'
}
let text = $(el).text()
let clone = $(el)
.clone()
.html('')
.attr('class', cls)
.attr('title', text)
if (cls === 'view-site') {
clone.attr('target', '_blank')
}
linksContainer.append(clone)
})
},
removeUserTools: function () {
$('#user-tools-sidebar').remove()
},
fetchData: function () {
let self = this
$.getJSON(this.appListUrl, function (data) {
self.render(data)
self.Dispatcher.emit('onMenuReady')
}).fail(function (err) {
console.error(err.responseText)
self.menu.remove()
$('#content')
.removeClass('col-md-9')
.removeClass('col-lg-10')
.css('flex-grow', 1)
self.Dispatcher.emit('onMenuError')
})
},
setHeight: function () {
let height = $(window).height() - $('#header').height() - 19 // nav padding and border
this.menu.css('min-height', height + 'px')
$('#content').css('padding-bottom', $('#footer').height() + 20 + 'px')
},
render: function (data) {
let self = this
let mainUl = $('<ul/>', { class: 'depth-0' }).appendTo(self.menu)
data.forEach((voice, index) => {
let active = false
if (voice.type === 'free') {
if (voice.re) {
let re = new RegExp(voice.re)
active = re.test(location.pathname)
} else {
active = location.pathname === voice.url
}
} else {
if (voice.url) {
let pathRexp = new RegExp(voice.url)
active = pathRexp.test(location.pathname)
}
}
let li = $('<li />', {
class:
'top-level ' +
voice.type +
(voice.defaultOpen ? ' default-open' : '') +
(active ? ' active' : '')
})
let a = $('<' + (voice.url ? 'a' : 'span') + ' />', {
href: voice.url || '#'
})
.text(voice.label)
.appendTo(li)
// icon
if (voice.icon) {
$('<i />', { class: voice.icon }).prependTo(a)
}
let subUl
if (voice.children && voice.children.length) {
subUl = $('<ul />', { class: 'depth-1' }).appendTo(li)
a.addClass('has-children')
voice.children.forEach((model, i) => {
let active = false
if (model.type === 'free') {
if (model.re) {
let re = new RegExp(model.re)
active = re.test(location.pathname)
} else {
active = location.pathname === model.url
}
} else if (model.url) {
let pathRexp = new RegExp(model.url)
active = pathRexp.test(location.pathname)
}
let subLi = $('<li />')
if (active) {
subLi.addClass('active')
li.addClass('with-active')
}
let a = $('<a />', {
href: model.url
})
.text(model.label)
.appendTo(subLi)
// icon
if (model.icon) {
$('<i />', { class: model.icon }).prependTo(a)
}
subLi.appendTo(subUl)
})
}
li.appendTo(mainUl)
})
$('.has-children').on('click', function (evt) {
evt.preventDefault()
let self = this
let p = $(this).parent()
let depth0 = $('.depth-0')
let depth1 = p.children('ul')
if (
p.hasClass('open') ||
(p.hasClass('default-open') && !$('body').hasClass('menu-open'))
) {
p.removeClass('open default-open')
depth1.children('.nav-back').remove()
depth0.css('overflow', 'auto')
} else {
if (p.hasClass('top-level')) {
$('.top-level').removeClass('open')
$('.top-level')
.find('.nav-back')
.remove()
}
p.addClass('open')
let back = $(
'<li class="nav-item nav-back"><a href="#"><i class="fa fa-angle-double-left"></i> ' + // eslint-disable-line
$(this).text() +
'</a></li>'
)
back.on('click', function () {
$(self).trigger('click')
})
depth1.prepend(back)
depth0.css('overflow', 'hidden')
depth0.scrollTop(0) // return to top
}
})
}
}
export default Menu

View File

@@ -0,0 +1,72 @@
import $ from 'jquery'
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
let Messages = {
/**
* Messages
*
* If toast are enabled, moves messages ul lis in toasts
*/
init: function (opts) {
if (opts.messagesToasts) {
let toasts = []
let all = true
$('.messagelist li').each((index, el) => {
let lv = $(el).attr('class')
if (opts.messagesToasts === true || opts.messagesToasts.indexOf(lv) !== -1) {
toasts.push(this.createToast($(el).attr('class'), $(el).html()))
$(el).remove()
} else {
all = false
}
})
if (toasts.length) {
$('<div />', {'class': 'toast-container position-absolute top-0 end-0 p-3'})
.append(toasts).appendTo($(document.body))
}
if (all) {
$('.messagelist').remove()
}
}
const toastElList = [].slice.call(document.querySelectorAll('.toast'))
toastElList.map(function (toastEl) {
new bootstrap.Toast(toastEl, { autohide: false }).show()
})
},
levelsMap: {
info: {
bg: 'info',
icon: 'fa fa-info-circle',
iconColor: '#fff'
},
success: {
bg: 'success',
icon: 'fa fa-check-circle',
iconColor: '#fff'
},
warning: {
bg: 'warning',
icon: 'fa fa-exclamation-circle',
iconColor: '#fff'
},
error: {
bg: 'danger',
icon: 'fa fa-exclamation-circle',
iconColor: '#fff'
}
},
createToast (level, content) {
let toast = `
<div class="toast d-flex align-items-center text-white bg-${this.levelsMap[level].bg} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-body">
<i class="${this.levelsMap[level].icon}" style="color: ${this.levelsMap[level].iconColor}; margin-right: .5rem"></i>
${content}
</div>
<button type="button" class="btn-close btn-close-white ms-auto me-2" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`
return $(toast)
}
}
export default Messages

View File

@@ -0,0 +1,155 @@
import $ from 'jquery'
import Translator from './i18n'
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
class Modal {
constructor (config) {
this.t = new Translator($('html').attr('lang'))
this.opts = {
subtitle: '',
hideFooter: false,
showBackBtn: false,
backBtnCb: function () {},
actionBtnLabel: this.t.get('save'),
actionBtnCb: null,
onUrlLoaded: function () {},
size: 'lg',
onClose: function () {}
}
this.isOpen = false
this.create() // adds modal, modalObj and events
this.update(config)
}
create () {
this.modalObj = $('<div />', {'class': 'modal fade'}).appendTo(document.body)
this.modalObj.html(`
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<div style="display: flex;">
<button type="button" class="back me-1" aria-label="Back">
<i class="fa fa-angle-left"></i>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">${this.t.get('close')}</button>
<button type="button" class="btn btn-primary btn-action"></button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
`)
var self = this
this.modalObj.on('hidden.bs.modal', function () {
self.close()
})
this.modal = new bootstrap.Modal(this.modalObj[0])
}
update (config) {
this.options = $.extend({}, this.opts, config)
this.setSize()
this.setHeader()
this.setTitle()
this.setSubtitle()
this.setContent()
this.setButtons()
}
setSize () {
this.modalObj.find('.modal-dialog').addClass('modal-' + this.options.size)
}
setHeader () {
if (this.options.showBackBtn) {
this.modalObj.find('.modal-header .back').show()
this.modalObj
.find('.modal-header .back')
.on('click', this.options.backBtnCb)
} else {
this.modalObj.find('.modal-header .back').hide()
}
}
setTitle () {
if (typeof this.options.title !== 'undefined') {
this.modalObj.find('.modal-title').html(this.options.title)
}
}
setSubtitle () {
if (this.options.subtitle) {
this.modalObj
.find('.modal-subtitle')
.show()
.html(this.options.subtitle)
} else {
this.modalObj
.find('.modal-subtitle')
.hide()
.html('')
}
}
setContent () {
var self = this
if (typeof this.options.url !== 'undefined') {
this.method = 'request'
$.get(this.options.url, function (response) {
self.modalObj.find('.modal-body').html(response)
self.options.onUrlLoaded(self)
})
} else if (this.options.content instanceof jQuery) {
self.modalObj.find('.modal-body').append(this.options.content)
} else if (typeof this.options.content !== 'undefined') {
self.modalObj.find('.modal-body').html(this.options.content)
}
};
setButtons () {
if (this.options.hideFooter) {
this.modalObj.find('.modal-footer').hide()
} else {
if (this.options.actionBtnCb) {
this.modalObj.find('.btn-action').text(this.options.actionBtnLabel)
this.modalObj.find('.btn-action').on('click', this.options.actionBtnCb)
} else {
this.modalObj.find('.btn-action').hide()
}
}
}
open () {
if (this.isOpen) {
return
}
this.toggle()
this.isOpen = true
}
toggle () {
this.modal[this.isOpen ? 'hide' : 'show']()
this.isOpen = !this.isOpen
}
close () {
if (!this.isOpen) {
return
}
this.modal.hide()
this.options.onClose()
this.isOpen = false
}
}
export default Modal

View File

@@ -0,0 +1,44 @@
import $ from 'jquery'
let Navbar = {
/**
* Navbar component
*
* Adds a menu toggler for mobile and does some styling
*/
init: function (config) {
this.menuAlwaysCollapsed = config.menuAlwaysCollapsed
this.fixNodes()
},
fixNodes: function () {
if (!this.menuAlwaysCollapsed) {
$('#header').addClass('expand')
} else {
$('#header').addClass('menu-always-collapsed')
}
// insert burger
$('#branding').before(
$('<button/>', {
'class': 'navbar-toggler navbar-toggler-right',
'data-bs-toggle': 'collapse'
}).html('<i class="fa fa-bars"></i>')
.click(() => $(document.body).addClass('menu-open')))
// remove only text
$('#user-tools')
.contents().filter(function () {
return (this.nodeType === 3)
}).remove()
// dropdown
let dropdown = $('<div/>', { 'class': 'dropdown' }).appendTo($('#user-tools'))
let dropdownMenu = $('<div/>', { 'class': 'dropdown-menu dropdown-menu-right' }).appendTo(dropdown)
$('#user-tools strong')
.addClass('dropdown-toggle btn btn-default')
.attr('data-bs-toggle', 'dropdown')
.prependTo(dropdown)
// @TODO find a way to mv view site from dropdown
// password change view doesn't have it so breaks things
$('#user-tools > a').addClass('dropdown-item').appendTo(dropdownMenu)
}
}
export default Navbar

View File

@@ -0,0 +1,9 @@
import $ from 'jquery'
let PasswordChange = {
init: function () {
$('body').addClass('passwordchange')
}
}
export default PasswordChange

View File

@@ -0,0 +1,163 @@
import $ from 'jquery'
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
let Tabs = {
/**
* Tabs component
*/
init: function (Dispatcher) {
this.Dispatcher = Dispatcher
if (this.shouldRun()) {
console.info('Baton:', 'generating tabs')
this.main.attr('data-baton-tab', 'main-tab')
this.createNav()
this.createPanes()
this.checkHash()
this.showErrors()
this.Dispatcher.emit('onTabsReady')
}
},
shouldRun: function () {
this.main = $('#content-main form .baton-tabs-init')
return this.main.length === 1
},
createNav: function () {
this.mainOrder = 0
this.tabsEl = []
this.domTabsEl = []
let classes = this.main.attr('class')
classes.split(' ').forEach((cl) => {
if (/baton-tab-/.test(cl)) {
this.tabsEl.push(cl.substring(10))
}
if (/order-/.test(cl)) {
this.mainOrder = parseInt(cl.replace('order-', ''))
}
})
let currentOrder = this.mainOrder ? 0 : this.mainOrder + 1
this.nav = $('<ul />', { 'class': 'nav nav-tabs' })
$('<li />', { 'class': 'nav-item' })
.css('order', this.mainOrder)
.append($('<a />', {
'class': 'nav-link' + (this.mainOrder === 0 ? ' active' : ''),
'data-bs-toggle': 'tab',
'data-bs-target': '#main-tab'
// href: '#main-tab'
}).text(this.main.children('h2').hide().text()))
.appendTo(this.nav)
this.tabsEl.forEach((el) => {
let domEl
if (/^group-/.test(el)) {
domEl = $('<div />').attr('data-baton-tab', el)
let items = el.substr(6).split('--')
items.forEach((item) => {
let e
if (/^inline-/.test(item)) {
e = this.createInlineEl(item)
} else {
e = this.createFieldsetEl(item)
}
domEl.append(e)
})
} else if (/^inline-/.test(el)) {
domEl = this.createInlineEl(el, true)
} else {
domEl = this.createFieldsetEl(el, true)
}
this.domTabsEl.push(domEl)
$('<li />', { 'class': 'nav-item ' })
.css('order', currentOrder)
.append($('<a />', {
'class': 'nav-link ' + (currentOrder === 0 ? ' active' : ''),
'data-bs-toggle': 'tab',
'data-bs-target': '#' + el
// href: '#' + el
}).text(domEl.find('h2:first-child').first().hide().text()))
.appendTo(this.nav)
currentOrder += 1
if (currentOrder === this.mainOrder) {
currentOrder += 1
}
})
this.main.before(this.nav)
$('a[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) {
// add hash to stay in same tab when save and continue
const hash = $(e.target).attr('data-bs-target')
window.location.replace(hash) // adding with replace won't add an history entry
let tooltipTriggerList = [].slice.call($('[title]:not(iframe)'))
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
})
},
createInlineEl: function (el, setDataTab = false) {
let domEl
if ($('#' + el.substring(7) + '_set-group').length) { // no related_name
domEl = $('#' + el.substring(7) + '_set-group')
} else {
domEl = $('#' + el.substring(7) + '-group')
}
if (setDataTab) {
domEl.attr('data-baton-tab', el)
}
return domEl
},
createFieldsetEl: function (el, setDataTab = false) {
let domEl = $('.tab-' + el)
if (setDataTab) {
domEl.attr('data-baton-tab', el)
}
return domEl
},
createPanes: function () {
let self = this
this.tabContent = $('<div />', { 'class': 'tab-content' })
this.tabMain = $('<div />', {
'class': 'tab-pane fade' + (this.mainOrder === 0 ? ' active show' : ''),
'id': 'main-tab'
}).appendTo(this.tabContent)
this.main.parent().children(':not(.nav-tabs):not(.submit-row):not(.errornote):not(.tab-fs-none)')
.each((index, el) => {
$(el).appendTo(self.tabMain)
})
this.nav.after(this.tabContent)
let currentOrder = this.mainOrder ? 0 : this.mainOrder + 1
this.domTabsEl.forEach((el, index) => {
let tabPane = $('<div />', {
'class': 'tab-pane' + (currentOrder === 0 ? ' active show' : ''),
'id': self.tabsEl[index]
}).appendTo(this.tabContent)
el.appendTo(tabPane)
currentOrder += 1
if (currentOrder === this.mainOrder) {
currentOrder += 1
}
})
},
showErrors: function () {
let els = [this.main, ...this.domTabsEl]
for (let i = 0, len = els.length; i < len; i++) {
let el = els[i]
if (el.find('.form-row.errors, .errorlist').length) {
const tab = new bootstrap.Tab(this.nav.find('a[data-bs-target="#' + el.attr('data-baton-tab') + '"]')[0])
tab.show()
break
}
}
},
checkHash: function () {
if (location.hash && this.nav.find('a[data-bs-target="' + location.hash + '"]').length) {
const tab = new bootstrap.Tab(this.nav.find('a[data-bs-target="' + location.hash + '"]')[0])
tab.show()
}
}
}
export default Tabs

View File

@@ -0,0 +1,600 @@
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
// minimal template polyfill
(function() {
'use strict';
var needsTemplate = (typeof HTMLTemplateElement === 'undefined');
var brokenDocFragment = !(document.createDocumentFragment().cloneNode() instanceof DocumentFragment);
var needsDocFrag = false;
// NOTE: Replace DocumentFragment to work around IE11 bug that
// causes children of a document fragment modified while
// there is a mutation observer to not have a parentNode, or
// have a broken parentNode (!?!)
if (/Trident/.test(navigator.userAgent)) {
(function() {
needsDocFrag = true;
var origCloneNode = Node.prototype.cloneNode;
Node.prototype.cloneNode = function cloneNode(deep) {
var newDom = origCloneNode.call(this, deep);
if (this instanceof DocumentFragment) {
newDom.__proto__ = DocumentFragment.prototype;
}
return newDom;
};
// IE's DocumentFragment querySelector code doesn't work when
// called on an element instance
DocumentFragment.prototype.querySelectorAll = HTMLElement.prototype.querySelectorAll;
DocumentFragment.prototype.querySelector = HTMLElement.prototype.querySelector;
Object.defineProperties(DocumentFragment.prototype, {
'nodeType': {
get: function () {
return Node.DOCUMENT_FRAGMENT_NODE;
},
configurable: true
},
'localName': {
get: function () {
return undefined;
},
configurable: true
},
'nodeName': {
get: function () {
return '#document-fragment';
},
configurable: true
}
});
var origInsertBefore = Node.prototype.insertBefore;
function insertBefore(newNode, refNode) {
if (newNode instanceof DocumentFragment) {
var child;
while ((child = newNode.firstChild)) {
origInsertBefore.call(this, child, refNode);
}
} else {
origInsertBefore.call(this, newNode, refNode);
}
return newNode;
}
Node.prototype.insertBefore = insertBefore;
var origAppendChild = Node.prototype.appendChild;
Node.prototype.appendChild = function appendChild(child) {
if (child instanceof DocumentFragment) {
insertBefore.call(this, child, null);
} else {
origAppendChild.call(this, child);
}
return child;
};
var origRemoveChild = Node.prototype.removeChild;
var origReplaceChild = Node.prototype.replaceChild;
Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
if (newChild instanceof DocumentFragment) {
insertBefore.call(this, newChild, oldChild);
origRemoveChild.call(this, oldChild);
} else {
origReplaceChild.call(this, newChild, oldChild);
}
return oldChild;
};
Document.prototype.createDocumentFragment = function createDocumentFragment() {
var frag = this.createElement('df');
frag.__proto__ = DocumentFragment.prototype;
return frag;
};
var origImportNode = Document.prototype.importNode;
Document.prototype.importNode = function importNode(impNode, deep) {
deep = deep || false;
var newNode = origImportNode.call(this, impNode, deep);
if (impNode instanceof DocumentFragment) {
newNode.__proto__ = DocumentFragment.prototype;
}
return newNode;
};
})();
}
// NOTE: we rely on this cloneNode not causing element upgrade.
// This means this polyfill must load before the CE polyfill and
// this would need to be re-worked if a browser supports native CE
// but not <template>.
var capturedCloneNode = Node.prototype.cloneNode;
var capturedCreateElement = Document.prototype.createElement;
var capturedImportNode = Document.prototype.importNode;
var capturedRemoveChild = Node.prototype.removeChild;
var capturedAppendChild = Node.prototype.appendChild;
var capturedReplaceChild = Node.prototype.replaceChild;
var capturedParseFromString = DOMParser.prototype.parseFromString;
var capturedHTMLElementInnerHTML = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'innerHTML') || {
/**
* @this {!HTMLElement}
* @return {string}
*/
get: function() {
return this.innerHTML;
},
/**
* @this {!HTMLElement}
* @param {string}
*/
set: function(text) {
this.innerHTML = text;
}
};
var capturedChildNodes = Object.getOwnPropertyDescriptor(window.Node.prototype, 'childNodes') || {
/**
* @this {!Node}
* @return {!NodeList}
*/
get: function() {
return this.childNodes;
}
};
var elementQuerySelectorAll = Element.prototype.querySelectorAll;
var docQuerySelectorAll = Document.prototype.querySelectorAll;
var fragQuerySelectorAll = DocumentFragment.prototype.querySelectorAll;
var scriptSelector = 'script:not([type]),script[type="application/javascript"],script[type="text/javascript"]';
function QSA(node, selector) {
// IE 11 throws a SyntaxError with `scriptSelector` if the node has no children due to the `:not([type])` syntax
if (!node.childNodes.length) {
return [];
}
switch (node.nodeType) {
case Node.DOCUMENT_NODE:
return docQuerySelectorAll.call(node, selector);
case Node.DOCUMENT_FRAGMENT_NODE:
return fragQuerySelectorAll.call(node, selector);
default:
return elementQuerySelectorAll.call(node, selector);
}
}
// returns true if nested templates cannot be cloned (they cannot be on
// some impl's like Safari 8 and Edge)
// OR if cloning a document fragment does not result in a document fragment
var needsCloning = (function() {
if (!needsTemplate) {
var t = document.createElement('template');
var t2 = document.createElement('template');
t2.content.appendChild(document.createElement('div'));
t.content.appendChild(t2);
var clone = t.cloneNode(true);
return (clone.content.childNodes.length === 0 || clone.content.firstChild.content.childNodes.length === 0
|| brokenDocFragment);
}
})();
var TEMPLATE_TAG = 'template';
var PolyfilledHTMLTemplateElement = function() {};
if (needsTemplate) {
var contentDoc = document.implementation.createHTMLDocument('template');
var canDecorate = true;
var templateStyle = document.createElement('style');
templateStyle.textContent = TEMPLATE_TAG + '{display:none;}';
var head = document.head;
head.insertBefore(templateStyle, head.firstElementChild);
/**
Provides a minimal shim for the <template> element.
*/
PolyfilledHTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
// if elements do not have `innerHTML` on instances, then
// templates can be patched by swizzling their prototypes.
var canProtoPatch =
!(document.createElement('div').hasOwnProperty('innerHTML'));
/**
The `decorate` method moves element children to the template's `content`.
NOTE: there is no support for dynamically adding elements to templates.
*/
PolyfilledHTMLTemplateElement.decorate = function(template) {
// if the template is decorated or not in HTML namespace, return fast
if (template.content ||
template.namespaceURI !== document.documentElement.namespaceURI) {
return;
}
template.content = contentDoc.createDocumentFragment();
var child;
while ((child = template.firstChild)) {
capturedAppendChild.call(template.content, child);
}
// NOTE: prefer prototype patching for performance and
// because on some browsers (IE11), re-defining `innerHTML`
// can result in intermittent errors.
if (canProtoPatch) {
template.__proto__ = PolyfilledHTMLTemplateElement.prototype;
} else {
template.cloneNode = function(deep) {
return PolyfilledHTMLTemplateElement._cloneNode(this, deep);
};
// add innerHTML to template, if possible
// Note: this throws on Safari 7
if (canDecorate) {
try {
defineInnerHTML(template);
defineOuterHTML(template);
} catch (err) {
canDecorate = false;
}
}
}
// bootstrap recursively
PolyfilledHTMLTemplateElement.bootstrap(template.content);
};
// Taken from https://github.com/jquery/jquery/blob/73d7e6259c63ac45f42c6593da8c2796c6ce9281/src/manipulation/wrapMap.js
var topLevelWrappingMap = {
'option': ['select'],
'thead': ['table'],
'col': ['colgroup', 'table'],
'tr': ['tbody', 'table'],
'th': ['tr', 'tbody', 'table'],
'td': ['tr', 'tbody', 'table']
};
var getTagName = function(text) {
// Taken from https://github.com/jquery/jquery/blob/73d7e6259c63ac45f42c6593da8c2796c6ce9281/src/manipulation/var/rtagName.js
return ( /<([a-z][^/\0>\x20\t\r\n\f]+)/i.exec(text) || ['', ''])[1].toLowerCase();
};
var defineInnerHTML = function defineInnerHTML(obj) {
Object.defineProperty(obj, 'innerHTML', {
get: function() {
return getInnerHTML(this);
},
set: function(text) {
// For IE11, wrap the text in the correct (table) context
var wrap = topLevelWrappingMap[getTagName(text)];
if (wrap) {
for (var i = 0; i < wrap.length; i++) {
text = '<' + wrap[i] + '>' + text + '</' + wrap[i] + '>';
}
}
contentDoc.body.innerHTML = text;
PolyfilledHTMLTemplateElement.bootstrap(contentDoc);
while (this.content.firstChild) {
capturedRemoveChild.call(this.content, this.content.firstChild);
}
var body = contentDoc.body;
// If we had wrapped, get back to the original node
if (wrap) {
for (var j = 0; j < wrap.length; j++) {
body = body.lastChild;
}
}
while (body.firstChild) {
capturedAppendChild.call(this.content, body.firstChild);
}
},
configurable: true
});
};
var defineOuterHTML = function defineOuterHTML(obj) {
Object.defineProperty(obj, 'outerHTML', {
get: function() {
return '<' + TEMPLATE_TAG + '>' + this.innerHTML + '</' + TEMPLATE_TAG + '>';
},
set: function(innerHTML) {
if (this.parentNode) {
contentDoc.body.innerHTML = innerHTML;
var docFrag = this.ownerDocument.createDocumentFragment();
while (contentDoc.body.firstChild) {
capturedAppendChild.call(docFrag, contentDoc.body.firstChild);
}
capturedReplaceChild.call(this.parentNode, docFrag, this);
} else {
throw new Error("Failed to set the 'outerHTML' property on 'Element': This element has no parent node.");
}
},
configurable: true
});
};
defineInnerHTML(PolyfilledHTMLTemplateElement.prototype);
defineOuterHTML(PolyfilledHTMLTemplateElement.prototype);
/**
The `bootstrap` method is called automatically and "fixes" all
<template> elements in the document referenced by the `doc` argument.
*/
PolyfilledHTMLTemplateElement.bootstrap = function bootstrap(doc) {
var templates = QSA(doc, TEMPLATE_TAG);
for (var i=0, l=templates.length, t; (i<l) && (t=templates[i]); i++) {
PolyfilledHTMLTemplateElement.decorate(t);
}
};
// auto-bootstrapping for main document
document.addEventListener('DOMContentLoaded', function() {
PolyfilledHTMLTemplateElement.bootstrap(document);
});
// Patch document.createElement to ensure newly created templates have content
Document.prototype.createElement = function createElement() {
var el = capturedCreateElement.apply(this, arguments);
if (el.localName === 'template') {
PolyfilledHTMLTemplateElement.decorate(el);
}
return el;
};
DOMParser.prototype.parseFromString = function() {
var el = capturedParseFromString.apply(this, arguments);
PolyfilledHTMLTemplateElement.bootstrap(el);
return el;
};
Object.defineProperty(HTMLElement.prototype, 'innerHTML', {
get: function() {
return getInnerHTML(this);
},
set: function(text) {
capturedHTMLElementInnerHTML.set.call(this, text);
PolyfilledHTMLTemplateElement.bootstrap(this);
},
configurable: true,
enumerable: true
});
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#escapingString
var escapeAttrRegExp = /[&\u00A0"]/g;
var escapeDataRegExp = /[&\u00A0<>]/g;
var escapeReplace = function(c) {
switch (c) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
case '\u00A0':
return '&nbsp;';
}
};
var escapeAttr = function(s) {
return s.replace(escapeAttrRegExp, escapeReplace);
};
var escapeData = function(s) {
return s.replace(escapeDataRegExp, escapeReplace);
};
var makeSet = function(arr) {
var set = {};
for (var i = 0; i < arr.length; i++) {
set[arr[i]] = true;
}
return set;
};
// http://www.whatwg.org/specs/web-apps/current-work/#void-elements
var voidElements = makeSet([
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr'
]);
var plaintextParents = makeSet([
'style',
'script',
'xmp',
'iframe',
'noembed',
'noframes',
'plaintext',
'noscript'
]);
/**
* @param {Node} node
* @param {Node} parentNode
* @param {Function=} callback
*/
var getOuterHTML = function(node, parentNode, callback) {
switch (node.nodeType) {
case Node.ELEMENT_NODE: {
var tagName = node.localName;
var s = '<' + tagName;
var attrs = node.attributes;
for (var i = 0, attr; (attr = attrs[i]); i++) {
s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
}
s += '>';
if (voidElements[tagName]) {
return s;
}
return s + getInnerHTML(node, callback) + '</' + tagName + '>';
}
case Node.TEXT_NODE: {
var data = /** @type {Text} */ (node).data;
if (parentNode && plaintextParents[parentNode.localName]) {
return data;
}
return escapeData(data);
}
case Node.COMMENT_NODE: {
return '<!--' + /** @type {Comment} */ (node).data + '-->';
}
default: {
window.console.error(node);
throw new Error('not implemented');
}
}
};
/**
* @param {Node} node
* @param {Function=} callback
*/
var getInnerHTML = function(node, callback) {
if (node.localName === 'template') {
node = /** @type {HTMLTemplateElement} */ (node).content;
}
var s = '';
var c$ = callback ? callback(node) : capturedChildNodes.get.call(node);
for (var i=0, l=c$.length, child; (i<l) && (child=c$[i]); i++) {
s += getOuterHTML(child, node, callback);
}
return s;
};
}
// make cloning/importing work!
if (needsTemplate || needsCloning) {
PolyfilledHTMLTemplateElement._cloneNode = function _cloneNode(template, deep) {
var clone = capturedCloneNode.call(template, false);
// NOTE: decorate doesn't auto-fix children because they are already
// decorated so they need special clone fixup.
if (this.decorate) {
this.decorate(clone);
}
if (deep) {
// NOTE: use native clone node to make sure CE's wrapped
// cloneNode does not cause elements to upgrade.
capturedAppendChild.call(clone.content, capturedCloneNode.call(template.content, true));
// now ensure nested templates are cloned correctly.
fixClonedDom(clone.content, template.content);
}
return clone;
};
// Given a source and cloned subtree, find <template>'s in the cloned
// subtree and replace them with cloned <template>'s from source.
// We must do this because only the source templates have proper .content.
var fixClonedDom = function fixClonedDom(clone, source) {
// do nothing if cloned node is not an element
if (!source.querySelectorAll) return;
// these two lists should be coincident
var s$ = QSA(source, TEMPLATE_TAG);
if (s$.length === 0) {
return;
}
var t$ = QSA(clone, TEMPLATE_TAG);
for (var i=0, l=t$.length, t, s; i<l; i++) {
s = s$[i];
t = t$[i];
if (PolyfilledHTMLTemplateElement && PolyfilledHTMLTemplateElement.decorate) {
PolyfilledHTMLTemplateElement.decorate(s);
}
capturedReplaceChild.call(t.parentNode, cloneNode.call(s, true), t);
}
};
// make sure scripts inside of a cloned template are executable
var fixClonedScripts = function fixClonedScripts(fragment) {
var scripts = QSA(fragment, scriptSelector);
for (var ns, s, i = 0; i < scripts.length; i++) {
s = scripts[i];
ns = capturedCreateElement.call(document, 'script');
ns.textContent = s.textContent;
var attrs = s.attributes;
for (var ai = 0, a; ai < attrs.length; ai++) {
a = attrs[ai];
ns.setAttribute(a.name, a.value);
}
capturedReplaceChild.call(s.parentNode, ns, s);
}
};
// override all cloning to fix the cloned subtree to contain properly
// cloned templates.
var cloneNode = Node.prototype.cloneNode = function cloneNode(deep) {
var dom;
// workaround for Edge bug cloning documentFragments
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8619646/
if (!needsDocFrag && brokenDocFragment && this instanceof DocumentFragment) {
if (!deep) {
return this.ownerDocument.createDocumentFragment();
} else {
dom = importNode.call(this.ownerDocument, this, true);
}
} else if (this.nodeType === Node.ELEMENT_NODE &&
this.localName === TEMPLATE_TAG &&
this.namespaceURI == document.documentElement.namespaceURI) {
dom = PolyfilledHTMLTemplateElement._cloneNode(this, deep);
} else {
dom = capturedCloneNode.call(this, deep);
}
// template.content is cloned iff `deep`.
if (deep) {
fixClonedDom(dom, this);
}
return dom;
};
// NOTE: we are cloning instead of importing <template>'s.
// However, the ownerDocument of the cloned template will be correct!
// This is because the native import node creates the right document owned
// subtree and `fixClonedDom` inserts cloned templates into this subtree,
// thus updating the owner doc.
var importNode = Document.prototype.importNode = function importNode(element, deep) {
deep = deep || false;
if (element.localName === TEMPLATE_TAG) {
return PolyfilledHTMLTemplateElement._cloneNode(element, deep);
} else {
var dom = capturedImportNode.call(this, element, deep);
if (deep) {
fixClonedDom(dom, element);
fixClonedScripts(dom);
}
return dom;
}
};
}
if (needsTemplate) {
window.HTMLTemplateElement = PolyfilledHTMLTemplateElement;
}
})();

View File

@@ -0,0 +1,61 @@
import Translator from './i18n'
export function copyTextToClipboard (text) {
let t = new Translator($('html').attr('lang'))
var textArea = document.createElement('textarea')
//
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a flash,
// so some of these are just precautions. However in IE the element
// is visible whilst the popup box asking the user for permission for
// the web page to copy to the clipboard.
//
// Place in top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed'
textArea.style.top = 0
textArea.style.left = 0
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em'
textArea.style.height = '2em'
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0
// Clean up any borders.
textArea.style.border = 'none'
textArea.style.outline = 'none'
textArea.style.boxShadow = 'none'
// Avoid flash of white box if rendered for any reason.
textArea.style.background = 'transparent'
textArea.value = text
document.body.appendChild(textArea)
textArea.select()
try {
var successful = document.execCommand('copy')
var msg = successful ? 'successful' : 'unsuccessful'
console.log('Copying text command was ' + msg)
} catch (err) {
window.prompt(
t.get('cannotCopyToClipboardMessage'),
text
)
}
document.body.removeChild(textArea)
}

View File

@@ -0,0 +1,71 @@
export const messages = {
unsavedChangesAlert: {
en: 'You have some unsaved changes.',
it: 'Alcune modifiche non sono state salvate.'
},
uploading: {
en: 'Uploading...',
it: 'Uploading...'
},
filter: {
en: 'Filter',
it: 'Filtra'
},
close: {
en: 'Close',
it: 'Chiudi'
},
save: {
en: 'Save',
it: 'Salva'
},
search: {
en: 'Search',
it: 'Cerca'
},
cannotCopyToClipboardMessage: {
en: 'Cannot copy to clipboard, please do it manually: Ctrl+C, Enter',
it: 'Impossibile copiare negli appunti, copiare manualmente: Ctrl+C, Enter'
},
retrieveDataError: {
en: 'There was an error retrieving the data',
it: 'Si è verificato un errore nel reuperare i dati'
}
}
export default class Translator {
constructor (lng) {
this.lng = this.setLng(lng)
}
setLng (lng) {
if (lng === 'it' || /it-/.test(lng)) {
return 'it'
} else if (lng === 'en' || /en-/.test(lng)) {
return 'en'
}
return lng
}
get (key) {
// check custom translations first
let b = window.Baton
if (b.translations && b.translations[key] !== 'undefined') {
return b.translations[key]
}
// if key is not found, return empty string
if (typeof messages[key] === 'undefined') {
return ''
}
// search localized message
if (messages[key][this.lng] !== undefined) {
return messages[key][this.lng]
}
// default to english
return messages[key]['en']
}
}

View File

@@ -0,0 +1,14 @@
<svg width="15" height="60" viewBox="0 0 1792 7168" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="previous">
<path d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="next">
<path d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
</defs>
<use xlink:href="#previous" x="0" y="0" fill="#333333" />
<use xlink:href="#previous" x="0" y="1792" fill="#000000" />
<use xlink:href="#next" x="0" y="3584" fill="#333333" />
<use xlink:href="#next" x="0" y="5376" fill="#000000" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path fill="#666666" d="M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 655 B

View File

@@ -0,0 +1,3 @@
<svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path fill="#555555" d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"/>
</svg>

After

Width:  |  Height:  |  Size: 458 B

View File

@@ -0,0 +1,34 @@
<svg width="16" height="192" viewBox="0 0 1792 21504" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="up">
<path d="M1412 895q0-27-18-45l-362-362-91-91q-18-18-45-18t-45 18l-91 91-362 362q-18 18-18 45t18 45l91 91q18 18 45 18t45-18l189-189v502q0 26 19 45t45 19h128q26 0 45-19t19-45v-502l189 189q19 19 45 19t45-19l91-91q18-18 18-45zm252 1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="down">
<path d="M1412 897q0-27-18-45l-91-91q-18-18-45-18t-45 18l-189 189v-502q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v502l-189-189q-19-19-45-19t-45 19l-91 91q-18 18-18 45t18 45l362 362 91 91q18 18 45 18t45-18l91-91 362-362q18-18 18-45zm252-1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="left">
<path d="M1408 960v-128q0-26-19-45t-45-19h-502l189-189q19-19 19-45t-19-45l-91-91q-18-18-45-18t-45 18l-362 362-91 91q-18 18-18 45t18 45l91 91 362 362q18 18 45 18t45-18l91-91q18-18 18-45t-18-45l-189-189h502q26 0 45-19t19-45zm256-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="right">
<path d="M1413 896q0-27-18-45l-91-91-362-362q-18-18-45-18t-45 18l-91 91q-18 18-18 45t18 45l189 189h-502q-26 0-45 19t-19 45v128q0 26 19 45t45 19h502l-189 189q-19 19-19 45t19 45l91 91q18 18 45 18t45-18l362-362 91-91q18-18 18-45zm251 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="clearall">
<path transform="translate(336, 336) scale(0.75)" d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="chooseall">
<path transform="translate(336, 336) scale(0.75)" d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
</defs>
<use xlink:href="#up" x="0" y="0" fill="#666666" />
<use xlink:href="#up" x="0" y="1792" fill="#447e9b" />
<use xlink:href="#down" x="0" y="3584" fill="#666666" />
<use xlink:href="#down" x="0" y="5376" fill="#447e9b" />
<use xlink:href="#left" x="0" y="7168" fill="#666666" />
<use xlink:href="#left" x="0" y="8960" fill="#447e9b" />
<use xlink:href="#right" x="0" y="10752" fill="#666666" />
<use xlink:href="#right" x="0" y="12544" fill="#447e9b" />
<use xlink:href="#clearall" x="0" y="14336" fill="#666666" />
<use xlink:href="#clearall" x="0" y="16128" fill="#447e9b" />
<use xlink:href="#chooseall" x="0" y="17920" fill="#666666" />
<use xlink:href="#chooseall" x="0" y="19712" fill="#447e9b" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,19 @@
<svg width="14" height="84" viewBox="0 0 1792 10752" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="sort">
<path d="M1408 1088q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45zm0-384q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"/>
</g>
<g id="ascending">
<path d="M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"/>
</g>
<g id="descending">
<path d="M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"/>
</g>
</defs>
<use xlink:href="#sort" x="0" y="0" fill="#999999" />
<use xlink:href="#sort" x="0" y="1792" fill="#447e9b" />
<use xlink:href="#ascending" x="0" y="3584" fill="#999999" />
<use xlink:href="#ascending" x="0" y="5376" fill="#447e9b" />
<use xlink:href="#descending" x="0" y="7168" fill="#999999" />
<use xlink:href="#descending" x="0" y="8960" fill="#447e9b" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,110 @@
// jQuery is provided by webpack provider plugin
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
import './styles/baton.scss'
import Dispatcher from 'js-event-dispatcher/dist/EventDispatcher'
import Navbar from './core/Navbar'
import Footer from './core/Footer'
import Menu from './core/Menu'
import ActionResult from './core/ActionResult'
import PasswordChange from './core/PasswordChange'
import Analytics from './core/Analytics'
import Tabs from './core/Tabs'
import ChangeList from './core/ChangeList'
import ChangeForm from './core/ChangeForm'
import Login from './core/Login'
import AdminDocs from './core/AdminDocs'
import Filer from './core/Filer'
import Modal from './core/Modal'
import Messages from './core/Messages'
window.Baton = {
intialized: false,
init: function (config) {
console.info('Baton:', 'init')
console.info('Baton:', `rev ${BATON_REVISION}`)
this.initialized = true
let page = this.detectPageHook ? this.detectPageHook(this.page) : this.page()
$('body').addClass('page-' + page)
// toasts
Messages.init(config)
Navbar.init(config)
Dispatcher.emit('onNavbarReady')
if (page !== 'login' && page !== 'logout' && !/_popup/.test(location.search)) {
Menu.init(config, Dispatcher)
}
if (page === 'login') {
Login.init(config)
} else if (page === 'logout' || page === 'password_change_success') {
ActionResult.init()
} else if (page === 'password_change') {
PasswordChange.init()
} else if (page === 'changelist') {
ChangeList.init(config)
} else if (page === 'add_form' || page === 'change_form') {
ChangeForm.init(config)
} else if (page === 'admindocs') {
AdminDocs.init()
} else if (page === 'filer') {
Filer.init()
}
Footer.init({
remove: /_popup/.test(location.search)
})
// tabs
if (page === 'add_form' || page === 'change_form') {
Tabs.init(Dispatcher)
}
// tooltips
setTimeout(this.loadTooltips, 1000) // wait a bit for tinymce
console.info('Baton:', 'ready')
document.body.className += ' baton-ready'
if (config.menuAlwaysCollapsed) {
document.body.className += ' menu-mobile'
}
Dispatcher.emit('onReady')
},
loadTooltips: function () {
let tooltipTriggerList = [].slice.call($('[title]:not(iframe)'))
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
},
page: function () {
if (/^(\/[a-z]{2})?\/admin\/$/.test(location.pathname)) {
return 'dashboard'
} else if (/^(\/[a-z]{2})?\/admin\/doc\//.test(location.pathname)) {
return 'admindocs'
} else if (/^(\/[a-z]{2})?\/admin\/login\/$/.test(location.pathname)) {
return 'login'
} else if (/^(\/[a-z]{2})?\/admin\/logout\/$/.test(location.pathname)) {
return 'logout'
} else if (/^(\/[a-z]{2})?\/admin\/password_change\/$/.test(location.pathname)) {
return 'password_change'
} else if (/^(\/[a-z]{2})?\/admin\/password_change\/done\/$/.test(location.pathname)) {
return 'password_change_success'
} else if (/\/add\//.test(location.pathname)) {
return 'add_form'
} else if (/\/change\//.test(location.pathname)) {
return 'change_form'
} else if (document.getElementById('changelist')) {
return 'changelist'
} else if (document.getElementById('change-history') || /^(\/[a-z]{2})?\/admin\/[^/]+\/[^/]+\/[^/]+\/history/.test(location.pathname)) {
return 'changehistory'
} else if (/\/filer\//.test(location.pathname)) {
return 'filer'
} else {
return 'default'
}
},
Analytics: Analytics,
Dispatcher: Dispatcher,
Modal: Modal
}
window.jQuery = jQuery

View File

@@ -0,0 +1,19 @@
// scss-lint:disable IdSelector PlaceholderInExtend SelectorDepth NestingDepth
.actionresult {
#content {
h1 + p {
@extend .pt-3;
}
> p {
@extend .ps-3;
@extend .pe-3;
a {
@extend .btn;
@extend .btn-secondary;
}
}
}
}

View File

@@ -0,0 +1,115 @@
.page-admindocs {
.admindocs-body {
padding: 0 1rem;
#content-main {
padding-top: 1rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.4rem;
}
.subhead {
margin-bottom: 1rem;
}
.help {
@extend .alert;
@extend .alert-info;
&::before {
content: '\f05a';
display: inline-block;
font-family: 'Font Awesome\ 5 Free';
margin-right: .5rem;
}
}
.simple {
background: $admindocs-simple-bg;
padding-bottom: .5rem;
padding-top: 1rem;
}
pre {
background: $admindocs-pre-bg;
color: $admindocs-pre-color;
font-size: .8rem;
padding: 1rem;
}
table {
@extend .table;
border: $admindocs-table-border-color !important;
}
.module {
background: $admindocs-module-bg;
margin-bottom: 1rem;
max-width: 100%;
overflow: auto;
h2 {
background: $admindocs-module-header-bg;
border: 1px solid $admindocs-module-header-border-color;
font-size: 1.4rem;
padding: .5rem;
}
h3 {
color: $base;
font-size: 1.4rem;
}
h4 {
font-size: 1.2rem;
}
table {
@extend .table;
thead {
@extend .table-dark;
}
}
> h3, > p, > h4, > dl {
padding-left: .5rem;
padding-right: .5rem;
}
}
.small a {
@extend .btn;
@extend .btn-sm;
@extend .btn-secondary;
}
}
.colSM .admindocs-body {
display: flex;
flex-direction: column;
@include media-breakpoint-up(lg) {
flex-direction: row;
}
#content-main {
order: 2;
}
#content-related {
order: 1;
@include media-breakpoint-up(lg) {
margin-right: 1rem;
padding-top: 1rem;
}
}
}
}

View File

@@ -0,0 +1,19 @@
$white: #fff;
.container-analytics {
width: 100%;
h1 {
font-size: $h1-size;
}
h2 {
font-size: $h2-size;
}
.chart-container {
background: $chart-bg;
border: 1px solid $chart-border-color;
padding: 15px;
}
}

View File

@@ -0,0 +1,18 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth
.breadcrumbs {
@extend .p-3;
background: $breadcrumbs-bg;
// border-bottom: 1px solid $breadcrumbs-border-color;
line-height: 1.9;
word-spacing: 5px;
a {
&:first-child {
&::before {
@extend %font-awesome;
@extend .me-1;
content: '\f015';
}
}
}
}

View File

@@ -0,0 +1,195 @@
// scss-lint:disable all
/* CALENDARS & CLOCKS */
.calendarbox,
.clockbox {
margin: 5px auto;
font-size: 12px;
width: 19em;
text-align: center;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
overflow: hidden;
position: relative;
}
.clockbox {
width: auto;
h2 {
background: $clockbox-title-bg;
border-bottom: 1px solid $clockbox-title-border-color;
font-size: 1rem;
padding: 0.5rem 1rem;
}
}
.calendar {
margin: 0;
padding: 0;
}
.calendar table {
margin: 0;
padding: 0;
border-collapse: collapse;
background: white;
width: 100%;
caption {
caption-side: top;
}
}
.calendar caption,
.calendarbox h2 {
margin: 0;
text-align: center;
border-top: none;
background: #f5dd5d;
font-weight: 700;
font-size: 12px;
color: #333;
}
.calendar th {
padding: 8px 5px;
background: #f8f8f8;
border-bottom: 1px solid #ddd;
font-weight: 400;
font-size: 12px;
text-align: center;
color: #666;
}
.calendar td {
font-weight: 400;
font-size: 12px;
text-align: center;
padding: 0;
border-top: 1px solid #eee;
border-bottom: none;
}
.calendar td.selected a {
background: #79aec8;
color: #fff;
}
.calendar td.nonday {
background: #f8f8f8;
}
.calendar td.today a {
font-weight: 700;
}
.calendar td a,
.timelist a {
display: block;
font-weight: 400;
padding: 6px;
text-decoration: none;
color: #444;
}
.calendar td a:focus,
.timelist a:focus,
.calendar td a:hover,
.timelist a:hover {
background: #79aec8;
color: white;
}
.calendar td a:active,
.timelist a:active {
background: #417690;
color: white;
}
.calendarnav {
font-size: 10px;
text-align: center;
color: #ccc;
margin: 0;
padding: 1px 3px;
}
.calendarnav a:link,
#calendarnav a:visited,
#calendarnav a:focus,
#calendarnav a:hover {
color: #999;
}
.calendar-shortcuts {
background: white;
border-top: 1px solid #eee;
font-size: 11px;
line-height: 11px;
padding: 8px 0;
color: #ccc;
}
.calendarbox .calendarnav-previous,
.calendarbox .calendarnav-next {
display: block;
height: 15px;
padding: 0;
position: absolute;
text-indent: -9999px;
top: 14px;
width: 15px;
}
.calendarnav-previous {
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
left: 10px;
}
.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
background-position: 0 -15px;
}
.calendarnav-next {
right: 10px;
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}
.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
background-position: 0 -45px;
}
.calendar-cancel {
margin: 0;
padding: 4px 0;
font-size: 12px;
background: #eee;
border-top: 1px solid #ddd;
color: #333;
}
.calendar-cancel:focus,
.calendar-cancel:hover {
background: #ddd;
}
.calendar-cancel a {
color: black;
display: block;
}
ul.timelist,
.timelist li {
list-style-type: none;
margin: 0;
padding: 0;
}
.timelist a {
padding: 2px;
}

View File

@@ -0,0 +1,165 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth QualifyingElement SelectorDepth ImportantRule
.change-form {
#content {
form {
@extend %admin-form;
}
}
#content-main {
@extend .container-fluid;
}
.object-tools {
@extend .ps-0;
@extend .m-2;
float: right;
list-style-type: none;
.historylink {
&::before {
content: '\f017';
}
}
.viewsitelink {
&::before {
content: '\f26c';
}
}
}
.baton-image-preview {
height: 100px;
&:hover {
animation: bounce 0.1s;
animation-direction: alternate;
animation-iteration-count: 4;
}
}
.collapse-entry {
h3 {
cursor: pointer;
.entry-collapse-toggler {
margin-left: 1rem !important;
order: 1;
&::before {
color: $collapse-entry-icon-color;
content: '\f070';
cursor: pointer;
font-family: 'Font Awesome\ 5 Free';
}
& + span {
margin-left: auto;
}
}
&.entry-collapsed {
+ fieldset {
display: none !important;
}
.entry-collapse-toggler {
&::before {
content: '\f06e';
}
}
}
}
}
.collapse-entry .last-related, // do not collapse the newly added row
.expand-first .inline-related[id$="-0"] {
h3 {
.entry-collapse-toggler {
&::before {
content: '\f06e';
}
}
+ fieldset {
display: none !important;
}
&.entry-collapsed {
.entry-collapse-toggler {
&::before {
content: '\f070';
}
}
+ fieldset {
display: block !important;
}
}
}
}
}
.spinner-overlay {
align-items: center;
background: $spinner-overlay-bg;
bottom: 0;
color: $spinner-overlay-fg;
display: flex;
height: 100%;
justify-content: center;
left: 0;
position: fixed;
right: 0;
text-align: center;
top: 0;
width: 100%;
z-index: 100;
p {
font-size: 3rem;
font-weight: bold;
}
}
// tabs
.nav-tabs {
.nav-link {
background: #fafafa;
border: 1px solid #dee2e6;
color: #999;
cursor: pointer;
&:hover {
color: $gray-700;
text-decoration: none;
}
&.active {
font-weight: bold;
}
}
}
.submit-row + .nav-tabs {
margin-top: 1rem;
}
.tab-content {
// padding-top: 1rem;
}
.baton-form-include {
display: block;
min-width: 100%;
}
.baton-form-include-right {
display: inline !important;
min-width: auto !important;
}
.change-form #content-main>form .form-row>div.baton-form-include {
display: block;
}

View File

@@ -0,0 +1,33 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth QualifyingElement SelectorDepth ImportantRule
body.page-changehistory #content-main {
@extend .container-fluid;
> .module {
@extend .p-3;
@extend .table-responsive;
background: $changehistory-bg;
border: 1px solid $changehistory-border-color;
margin-top: 15px;
#change-history {
@extend .table;
@extend .table-bordered;
margin-bottom: 0;
thead > tr {
background: $rl-head-bg;
color: $rl-head-color;
}
tbody tr:nth-child(2n + 1) {
background: $rl-tr-odd-bg;
color: $rl-tr-odd-color;
}
td,
th {
padding: .3rem;
}
}
}
}

View File

@@ -0,0 +1,484 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth QualifyingElement SelectorDepth ImportantRule
.change-list {
#content-main {
@extend .container-fluid;
}
.object-tools {
.addlink {
&::before {
content: '\f067';
}
}
}
.select2-container {
z-index: 1070;
}
#changelist {
border: 1px solid $changelist-border-color;
clear: both;
display: flex;
flex-wrap: wrap;
overflow: hidden;
.changelist-form-container { // django 3.1.1
display: flex;
flex: 0 0 100%;
flex-wrap: wrap;
overflow: hidden;
}
// no object tools
&:first-child {
margin-top: 1rem;
}
.toplinks {
margin-bottom: 0;
padding-left: 0;
li {
background: rgba(0, 0, 0, .1);
display: inline-block;
padding: .2rem 1rem;
&.date-back {
background: $base;
a {
color: $white;
}
}
}
+ br {
display: none;
}
}
.xfull {
overflow: auto;
width: 100%;
.toplinks {
white-space: nowrap;
}
}
.date-icon {
&::before {
@extend %font-awesome;
content: '\f073';
}
}
.clock-icon {
&::before {
@extend %font-awesome;
content: '\f017';
}
}
#result_list {
input[type=text],
input[type=number],
input[type=email],
input[type=date] {
@extend .form-control;
display: inline;
height: 28px;
margin: 2px 0;
width: auto;
}
}
}
#toolbar {
@extend .mb-0;
@extend .p-3;
@include make-col(12);
background: $toolbar-bg;
order: 1;
}
#changelist-search {
label,
input {
display: inline-block !important;
}
label {
@extend .pe-2;
position: relative;
top: -2px;
text-align: right;
}
input[type=text] {
@extend .form-control;
@extend .form-control-sm;
max-width: 160px;
}
input[type=submit] {
@extend .btn;
@extend .btn-sm;
@extend .ms-2;
background: $toolbar-btn-bg;
margin-top: -3px;
max-width: 100px;
&:hover {
background: $toolbar-btn-hover-bg;
}
}
}
.action-counter {
display: block;
@include media-breakpoint-up(sm) {
display: inline;
}
}
.changelist-filter-toggler {
@extend .btn;
@extend .btn-info;
align-items: center;
cursor: pointer;
display: inline;
float: right;
font-size: 1rem;
margin-bottom: 1rem;
min-height: 39px;
&.active {
background: $accent;
border-color: $accent !important;
}
&.with-actions {
display: flex;
flex-direction: row;
float: none;
margin-bottom: 0;
position: absolute;
right: 16px;
top: 32px;
}
> span {
display: none;
@include media-breakpoint-up(sm) {
display: inline-block;
margin-left: .5rem;
}
}
}
#changelist-filter {
@include make-col(12);
background: $filters-bg;
max-width: 100%;
order: 3;
@include media-breakpoint-up(lg) {
@include make-col(0);
max-height: 200px;
max-width: 99%;
display: none;
}
h2 {
background: $filters-title-bg;
color: $filters-title-color;
font-size: $h2-size;
margin-bottom: 0;
padding: .5rem 1rem;
}
#changelist-filter-clear {
background: $filters-clear-bg;
font-size: .9rem;
margin-bottom: 0;
margin-top: 0;
padding: .5rem 1rem;
a {
color: $filters-title-color;
}
}
h3 {
font-size: $h3-size;
margin-top: 1rem;
padding: 0 1rem;
&:first-child {
margin-top: 0;
}
}
ul {
list-style-type: none;
padding-left: 15px;
padding-right: 15px;
.selected {
border-left: 5px solid $filter-selected-border-color;
margin-left: -15px;
padding-left: 10px;
}
.select2-container {
outline: 0 !important;
min-width: auto !important;
width: 100% !important;
}
.select2-selection {
max-width: 100% !important;
width: 226px !important;
}
.select2-container--focus,
.select2-selection--single:focus {
border-radius: 2px;
box-shadow: 0 0 0 0.25rem rgba(127, 27, 39, .25) !important;
}
}
}
&.changelist-filter-always-open {
#changelist-filter,
#changelist-form {
transition: none !important;
}
}
// show filters
&.changelist-filter-active {
#changelist-filter {
@include media-breakpoint-up(lg) {
@include make-col(2);
max-height: 100%;
display: block;
}
}
#changelist-form {
@include media-breakpoint-up(lg) {
@include make-col(10);
}
}
}
#changelist-filter-modal {
@extend .modal-body;
@extend #changelist-filter;
display: block;
max-width: 100% !important;
max-height: 100% !important;
order: 0 !important;
padding: .5rem 0 !important;
width: auto !important;
select {
margin-left: 0 !important;
width: auto !important;
}
> div,
> p {
display: block;
}
ul {
li {
.select2-container {
width: 100% !important;
}
.select2-selection {
border: 1px solid #ced4da;
outline: 0 !important;
width: 100% !important;
max-width: 100% !important;
}
.select2-container--focus,
.select2-selection--single:focus {
border-radius: 2px;
}
}
}
}
#changelist-form {
@include make-col(12);
background: $changelist-bg;
order: 2;
padding: 1rem !important;
position: relative; // filter icon
@include media-breakpoint-up(lg) {
@include make-col(12);
}
.actions {
@extend .mb-4;
label {
@extend .pe-2;
}
button {
@extend .btn;
@extend .btn-sm;
@extend .btn-light;
@extend .ms-2;
@extend .me-2;
}
.all,
.question,
.clear {
display: none;
}
}
select {
@extend .form-select;
@extend .form-select-sm;
-moz-appearance: none;
text-indent: .01px;
text-overflow: clip;
}
}
.results {
@extend .mt-2;
@extend .table-responsive;
background: $changelist-bg;
clear: both;
table {
@extend .table;
@extend .table-bordered;
thead > tr {
background: $rl-head-bg;
color: $rl-head-color;
}
tbody tr:nth-child(2n + 1) {
background: $rl-tr-odd-bg;
color: $rl-tr-odd-color;
}
tbody tr.selected, tbody tr.selected td, tbody tr.selected th {
background: $rl-tr-selected-bg !important;
color: $rl-tr-selected-color;
}
.action-checkbox-column {
width: 20px;
.text {
padding-right: 0;
}
}
th,
td {
padding: .3rem;
}
}
.sortoptions {
float: right;
}
th .text {
padding-right: 44px;
}
.sorted .sortoptions a {
background: url('../img/sorting-icons.svg') 0 0 no-repeat;
background-size: 14px auto;
display: inline-block;
height: 14px;
position: relative;
width: 14px;
&.sortremove::after {
color: $sort-icon-color;
content: '\\';
font-size: 18px;
font-weight: 200;
left: 3px;
position: absolute;
top: -6px;
}
&.descending {
background-position: 0 -56px;
top: 1px;
}
&.ascending {
background-position: 0 -42px;
}
}
}
.baton-cl-include-top {
padding-right: 50px;
@include media-breakpoint-up(sm) {
padding-right: 100px;
}
}
.baton-cl-include-above,
.baton-cl-include-below {
width: 100%;
}
.baton-cl-include-below {
order: 2;
}
.admindatefilter {
border-bottom: 0 !important;
margin-right: 15px;
padding-bottom: 0;
position: relative;
form > p {
display: flex;
flex-direction: row;
}
.datetimeshortcuts {
right: 2rem;
}
input[type='text'] {
@extend .form-control;
@extend .form-control-sm;
flex-grow: 1;
}
input[type='submit'],
input[type='reset'] {
@extend .btn-sm;
}
input[type='reset'] {
@extend .btn-secondary;
}
}
}

View File

@@ -0,0 +1,134 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth
@include media-breakpoint-up(lg) {
.menu-mobile {
#content {
flex: 0 0 100% !important;
max-width: 100% !important;
width: 100% !important;
}
}
}
#content {
background: $content-bg;
margin-right: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
padding-top: 0 !important;
position: relative;
@include media-breakpoint-down(md) {
flex: 0 0 100% !important;
max-width: 100% !important;
}
> h1 {
@extend .mb-0;
@extend .p-3;
background: $dashboard-title-bg;
font-size: 1.6rem;
font-weight: 700;
+ h2 {
@extend .mb-0;
@extend .px-3;
background: $dashboard-title-bg;
font-size: 1.2rem;
padding-bottom: 1rem;
}
}
.object-tools {
@extend .ps-0;
@extend .m-2;
float: right;
list-style-type: none;
li {
display: inline-block;
}
a {
@extend .btn;
@extend .btn-secondary;
&::before {
@extend %font-awesome;
}
}
}
}
.messagelist {
list-style-type: none;
padding: 0;
.success {
@extend .alert;
@extend .alert-success;
@extend .ps-3;
&::before {
@extend %font-awesome;
content: "\f00c";
}
}
.info {
@extend .alert;
@extend .alert-info;
@extend .ps-3;
&::before {
@extend %font-awesome;
content: "\f05a";
}
}
.warning {
@extend .alert;
@extend .alert-warning;
@extend .ps-3;
&::before {
@extend %font-awesome;
content: "\f12a";
}
}
.error {
@extend .alert;
@extend .alert-danger;
@extend .ps-3;
&::before {
@extend %font-awesome;
content: "\f071";
}
}
}
// modal
.modal-header {
align-items: center !important;
}
.modal-dialog button.back {
align-items: center;
background: $modal-back-btn-bg;
border: none;
border-radius: 50%;
color: $modal-back-btn-fg;
display: flex;
height: 24px;
justify-content: center;
position: relative;
top: -4px;
width: 24px;
&:focus {
border: none;
outline: none;
}
}

View File

@@ -0,0 +1,193 @@
// scss-lint:disable PlaceholderInExtend IdSelector SelectorDepth NestingDepth ImportantRule
.dashboard {
#content {
background: $dashboard-bg;
}
#content-main {
@include make-row();
float: left;
margin-left: 0 !important;
padding: 16px 0;
width: 100%;
@include media-breakpoint-up(lg) {
margin-left: -15px;
width: 75%;
}
> p {
@extend .ps-3;
@extend .pe-3;
}
> .module {
@include make-col-ready();
@include make-col(12);
@include media-breakpoint-up(lg) {
@include make-col(6);
}
@media(min-width: 1600px) {
@include make-col(4);
}
}
table {
@extend .table;
background: $dashboard-module-bg;
border: 1px solid $dashboard-module-border-color;
tbody {
font-size: .9rem;
}
th {
width: 100%;
}
td {
white-space: nowrap;
}
}
}
#content-related {
float: right;
margin-bottom: 2rem;
margin-right: 0 !important;
padding: 0 15px;
width: 100%;
@include media-breakpoint-up(lg) {
margin-right: 25px;
width: 23%;
}
h2 {
font-size: $h2-size;
}
h3 {
font-size: $h3-size;
}
> div {
@extend .p-3;
@extend .mt-3;
background: $dashboard-module-bg;
border: 1px solid $dashboard-module-border-color;
}
.actionlist {
font-size: .9rem;
}
}
caption {
@extend .ps-2;
@extend .pe-2;
background: $dashboard-caption-bg;
border: 1px solid $dashboard-module-border-color;
caption-side: top;
a {
color: $dashboard-caption-color;
}
}
.actionlist {
border-left: 5px solid #e7e7e7;
list-style-type: none;
margin-left: .5rem;
margin-top: 1.5rem;
padding-left: 0;
position: relative;
&::after {
background-image: linear-gradient(#e7e7e7 50%, rgba(255, 255, 255, 0) 0%);
background-position: center;
background-repeat: repeat-y;
background-size: 5px 13px;
bottom: -25px;
content: '';
height: 25px;
left: -5px;
position: absolute;
width: 5px;
}
li {
padding: 1.5rem;
padding-top: 0;
position: relative;
&::before {
background: $dashboard-icon-color;
border-radius: 50%;
color: $white !important;
display: inline-block;
height: 30px;
left: -18px;
line-height: 30px;
position: absolute;
text-align: center;
width: 30px;
}
&.addlink::before {
background: $dashboard-add-bg;
}
&.changelink::before {
background: $dashboard-change-bg;
}
&.deletelink::before {
background: $dashboard-delete-bg;
}
}
}
.addlink,
.viewlink,
.deletelink,
.changelink {
&::before {
@extend %font-awesome;
@extend .me-1;
color: $dashboard-icon-color;
white-space: nowrap;
}
}
.addlink {
&::before {
content: '\f067';
}
}
.changelink {
&::before {
content: '\f304';
}
}
.deletelink {
&::before {
content: '\f1f8';
}
}
.viewlink {
&::before {
content: '\f26c';
}
}
}

View File

@@ -0,0 +1,24 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth QualifyingElement SelectorDepth ImportantRule
.delete-confirmation-content {
@extend .m-3;
@extend .p-3;
background: $delete-confirmation-bg;
border: 1px solid $delete-confirmation-border-color;
form {
input[type=submit] {
@extend .btn;
@extend.btn-danger;
}
.cancel-link {
@extend .btn;
@extend.btn-success;
&::before {
@extend %font-awesome;
content: '\f0e2';
}
}
}
}

View File

@@ -0,0 +1,45 @@
.filter-files-field.js-filter-files {
width: 170px;
}
.filter-files-container .filter-search-wrapper {
width: auto !important;
}
.filebrowser {
.paginator {
span {
border: 0 !important;
width: auto !important;
}
}
}
.search-is-focused .filter-files-container {
position: relative;
}
form .form-row .filer-dropzone.filer-dropzone-mobile .filerFile {
display: flex;
justify-content: center;
}
.js-filer-dropzone .insertlinkButton {
padding-top: 0 !important;
}
.form-row .filer-dropzone .filerFile .related-lookup.related-lookup-change {
min-width: 36px;
&::before {
content: '' !important;
}
.edit-file {
margin-top: -10px;
}
}
form .form-row .filer-dropzone {
min-width: 100% !important;
}

View File

@@ -0,0 +1,20 @@
@font-face {
font-family: 'Dosis';
font-style: normal;
font-weight: 300;
src: url('../fonts/Dosis-Light.ttf') format('truetype');
}
@font-face {
font-family: 'Dosis';
font-style: normal;
font-weight: 400;
src: url('../fonts/Dosis-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Dosis';
font-style: normal;
font-weight: 700;
src: url('../fonts/Dosis-Bold.ttf') format('truetype');
}

View File

@@ -0,0 +1,34 @@
// scss-lint:disable PlaceholderInExtend IdSelector
.site-footer {
@extend .p-3;
background: $footer-bg;
border-top: 1px solid $footer-border-color;
bottom: 0;
color: $footer-color;
font-size: .9rem;
left: 0;
margin-top: 20px;
position: absolute;
width: 100%;
p {
margin-bottom: 5px;
margin-top: 5px;
}
a {
color: $footer-link-color;
&:hover {
color: $footer-link-color-hover;
}
}
.btn-light {
color: $footer-support-fg;
}
}
.login #footer {
display: none;
}

View File

@@ -0,0 +1,85 @@
// scss-lint:disable ImportantRule IdSelector SelectorDepth NestingDepth PlaceholderInExtend
body.login {
align-items: center;
background: $login-bg;
display: flex;
flex-direction: column;
justify-content: center;
padding: 1px 1rem 0;
#header #branding {
margin: auto !important;
}
}
.login {
#content {
padding-top: 1px !important;
}
#content-main + br.clear {
display: none;
}
#container {
border: 0 !important;
border-radius: 0;
max-width: 100%;
min-width: auto;
width: 24em;
.navbar-toggler {
display: none !important;
}
.submit-row {
padding: 10px 0;
text-align: center;
input {
@extend .btn;
@extend .btn-secondary;
display: inline-block;
}
}
form {
padding: 1rem;
.input-group-text {
justify-content: center;
width: 42px;
}
}
input[type='text'],
input[type='password'] {
@extend .form-control;
background: #fff !important;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.pwd-visibility-toggle {
color: $password-visibility-toggler-icon-fg;
cursor: pointer;
position: absolute;
right: 8px;
top: 11px;
z-index: 10;
}
.form-row {
margin-left: 0 !important;
margin-right: 0 !important;
position: relative;
}
.errornote {
@extend .alert;
@extend .alert-warning;
margin: 1rem 1rem 0;
padding: .5rem;
}
}
}

View File

@@ -0,0 +1,9 @@
.page-logout {
#header {
padding-left: 1rem;
}
.site-footer {
position: static;
}
}

View File

@@ -0,0 +1,56 @@
html {
background: $navbar-bg;
height: 100%;
}
body {
font-weight: 300;
min-height: 100%;
opacity: 0;
transition: opacity .2s linear;
&.baton-ready {
opacity: 1;
}
}
a {
text-decoration: none;
&:hover {
text-decoration: underline;
}
&.btn {
&:hover {
text-decoration: none;
}
}
}
button,
input,
optgroup,
select,
textarea {
font-family: $font-family-sans-serif;
}
input[type=checkbox] {
@extend .form-check-input;
}
.clear {
clear: both;
}
// remove django 3.1 sidebar
#toggle-nav-sidebar,
#nav-sidebar {
display: none !important;
}
@keyframes bounce {
0% { transform: translateY(0) rotate(0deg); }
100% { transform: translateY(-5px) rotate(5deg); }
}

View File

@@ -0,0 +1,438 @@
//scss-lint:disable PlaceholderInExtend NestingDepth ImportantRule SelectorDepth
.menu-open .sidebar-menu {
left: 0;
}
body:not(.menu-mobile) {
@include media-breakpoint-up(lg) {
.sidebar-menu {
height: auto;
left: 0;
position: static;
h1 {
i {
display: none !important;
}
}
}
}
}
.sidebar-menu {
background: $menu-bg;
color: $menu-color;
height: 100%;
left: -100%;
padding: 0;
position: fixed;
top: 0;
z-index: 100;
@include media-breakpoint-up(lg) {
overflow: auto;
}
#branding {
background: $menu-branding-bg;
border-bottom: 1px solid $menu-branding-bg;
padding-top: 0;
text-align: center;
width: 100%;
> h1 {
background: $menu-branding-bg;
}
a {
color: $menu-branding-color !important;
}
}
.toggle-menu {
cursor: pointer;
position: absolute;
right: 1rem;
top: 1.6rem;
}
#user-tools {
display: none;
}
#user-tools-sidebar {
padding: 1rem;
position: relative;
text-align: center;
.user-links {
height: 38px;
transition: height .2s linear;
}
.user-area-toggler {
cursor: pointer;
}
&.collapsed {
.user-links,
.user-info div {
display: none;
}
}
.gravatar-icon {
border: 2px solid $gravatar-border-color;
border-radius: 50%;
}
.user-info {
align-items: center;
display: flex;
flex-direction: column;
font-size: 1.2rem;
justify-content: center;
}
.user-links {
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
margin-top: .5rem;
}
a {
color: $menu-link-color !important;
&.view-site {
&::after {
content: '\f108';
font-family: 'Font Awesome\ 5 Free';
}
&[href*=doc] {
&::after {
content: '\f02d' !important;
font-family: 'Font Awesome\ 5 Free';
}
}
}
&.password {
&::after {
content: '\f084';
font-family: 'Font Awesome\ 5 Free';
}
}
&.logout {
&::after {
content: '\f2f5';
font-family: 'Font Awesome\ 5 Free';
font-size: 1.6rem;
}
}
&::after {
color: $user-links-fg;
font-size: 1.3rem;
display: inline-block;
margin: 0 .6rem;
}
}
}
.search-field-tool {
margin: .5rem 1rem;
position: relative;
input {
background: $search-field-input-bg;
border-color: $search-field-input-border-color;
color: $search-field-color;
padding-right: 30px;
&:focus {
box-shadow: 0 0 0 transparent;
outline: 0 none;
}
}
&::after {
color: $secondary;
content: '\f002';
font-family: 'Font Awesome\ 5 Free';
position: absolute;
right: .5rem;
top: .2rem;
}
&.loading {
&::after {
animation: fa-spin 2s linear infinite;
content: '\f1ce';
}
}
#admin-search-datalist {
background: $search-field-datalist-bg;
max-height: 50vh;
overflow: auto;
position: absolute;
width: 100%;
div {
align-items: center;
border-bottom: 1px solid darken($search-field-datalist-bg, 2%);
cursor: pointer;
display: flex;
flex-direction: row;
font-size: .9rem;
justify-content: space-between;
max-height: 50vh;
overflow: auto;
padding: .5rem;
&.selected {
background: $search-field-selected-bg;
color: $search-field-selected-color;
a {
color: $search-field-selected-color;
}
}
a {
color: $search-field-link-color;
text-decoration: none;
}
i {
color: $search-field-icon-color;
font-size: .9rem;
}
}
}
}
h1 {
@extend .clearfix;
background: $menu-mobile-title-bg;
// border-bottom: 2px solid $menu-mobile-title-border-color;
color: $menu-mobile-title-color;
font-size: 1.6rem;
margin-bottom: 0;
padding: 1rem 16px;
position: absolute;
top: 0;
width: 100%;
z-index: 12;
@include media-breakpoint-up(lg) {
background: $menu-title-bg;
position: static;
}
i {
cursor: pointer;
float: right;
font-weight: normal;
margin-right: 10px;
margin-top: 5px;
}
}
.depth-0,
.depth-1 {
background: $menu-bg;
height: 100%;
left: 0;
list-style-type: none;
overflow: auto;
padding: 0;
padding-top: 66px;
position: absolute;
top: 0;
width: 100%;
z-index: 10;
@include media-breakpoint-up(lg) {
height: auto;
padding-top: 0;
position: static;
}
li {
// border-bottom: 1px solid $menu-li-border-color;
padding: 0;
a,
span {
padding: 10px 20px;
}
}
a,
span {
color: $menu-link-color;
display: block;
outline: 0;
&:hover {
color: $menu-link-hover-color;
text-decoration: none;
}
&.has-children {
@extend .clearfix;
&::after {
@extend %font-awesome;
content: '\f105';
float: right;
}
}
i {
display: inline-block;
margin-right: 8px;
}
}
}
.depth-0 {
> li:not(.title) {
&:hover {
background: $menu-li-hover-bg;
}
}
.title {
background: $menu-voice-title-bg !important;
> span {
color: $menu-voice-title-color !important;
font-weight: bold;
text-transform: uppercase;
}
}
}
.depth-1 {
display: block;
margin-left: -100%;
transition: margin-left .0s linear;
@include media-breakpoint-up(lg) {
display: none;
margin-left: 0;
li {
padding-left: 10px;
}
}
}
.title > .depth-1 li {
@include media-breakpoint-up(lg) {
padding-left: 0;
}
}
.open {
@include media-breakpoint-up(lg) {
.has-children {
&::after {
content: '\f107' !important;
}
}
ul,
li {
background: $menu-selected-sub-bg !important;
}
}
> .depth-1 {
display: block;
}
.depth-1 {
margin-left: 0;
z-index: 10;
}
}
.default-open {
@include media-breakpoint-up(lg) {
.has-children {
&::after {
content: '\f107' !important;
}
}
ul,
li {
background: $menu-selected-sub-bg !important;
}
> .depth-1 {
display: block;
}
.depth-1 {
margin-left: 0;
z-index: 10;
}
}
}
.nav-back {
background: $menu-mobile-back-bg;
@include media-breakpoint-up(lg) {
display: none;
}
}
.with-active {
@include media-breakpoint-up(lg) {
.has-children {
&::after {
content: '\f107' !important;
}
}
ul,
li {
background: $menu-selected-sub-bg;
}
> .depth-1 {
display: block;
}
.depth-1 {
margin-left: 0;
z-index: 10;
}
}
}
.active:not(.with-active) {
@include media-breakpoint-up(lg) {
background: $menu-active-voice-bg !important;
border-left: 5px solid $menu-active-voice-border-color;
position: relative;
a {
margin-left: -5px;
}
}
}
}

View File

@@ -0,0 +1,106 @@
// scss-lint:disable IdSelector NestingDepth PlaceholderInExtend
body:not(.login):not(.page-logout) #header {
&.expand {
@extend .d-flex;
@extend .d-lg-none;
@extend .navbar-expand-lg;
}
}
body.page-logout #header {
.navbar-toggler {
display: none;
}
}
#header {
@extend .navbar;
@extend .navbar-dark;
@extend .bg-dark;
align-items: center;
background: $navbar-bg !important;
color: $navbar-color;
flex-direction: row !important;
padding: .6rem 0;
strong {
color: $navbar-color;
padding-left: 0;
&::before {
content: '\f007';
display: inline-block;
font-family: 'Font Awesome\ 5 Free';
margin-right: 5px;
}
}
a {
color: $navbar-link-color;
&:hover {
color: $navbar-link-color-hover;
text-decoration: none;
}
&.dropdown-item {
color: $navbar-dropdown-link-color;
}
}
#user-tools {
@extend .d-inline-flex;
> a {
@extend .navbar-text;
order: 2;
}
.dropdown-menu {
@include media-breakpoint-up(md) {
left: auto;
right: 0;
}
}
}
.navbar-toggler {
border-width: 0 !important;
margin-right: 5px;
}
.nav-menu.is-active {
background: $navbar-mobile-pane-bg;
}
}
#branding {
// @extend .nav-left;
@extend .navbar-brand;
@extend .me-auto;
display: inline-block;
padding-bottom: 0;
h1 {
font-size: 1.6rem;
}
}
#site-name {
// @extend .nav-item;
}
#user-tools {
// @extend .nav-right;
// @extend .nav-menu;
a {
// @extend .nav-item;
}
strong {
// @extend .button;
// @extend .is-primary;
}
}

View File

@@ -0,0 +1,56 @@
// scss-lint:disable PlaceholderInExtend
.paginator {
@extend .clearfix;
overflow: auto;
padding-top: .5rem;
white-space: nowrap;
span,
a:not(.showall) {
background: $pag-bg;
border: 1px solid $pag-border-color;
color: $pag-color;
display: inline-block;
height: 40px;
line-height: 40px;
padding: 0 !important;
text-align: center;
width: 40px;
&:hover {
background: $pag-hover-bg;
}
}
.end {
@extend .me-3;
}
.this-page {
background: $pag-selected-bg;
color: $pag-selected-color;
&:hover {
background: $pag-selected-bg;
}
}
.showall {
@extend .btn;
@extend .btn-sm;
@extend .btn-outline-primary;
@extend .ms-2;
margin-top: -5px;
}
input {
@extend .btn;
@extend .btn-success;
margin-top: -4px;
@include media-breakpoint-up(md) {
float: right;
margin-top: 0;
}
}
}

View File

@@ -0,0 +1,20 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth QualifyingElement SelectorDepth ImportantRule
.passwordchange,
.auth-user.change-form {
#content-main {
@extend .container-fluid;
> form {
@extend %admin-form;
}
.form-row {
align-items: flex-start;
display: flex;
flex: 1;
flex-direction: row;
flex-wrap: wrap;
}
}
}

View File

@@ -0,0 +1,867 @@
// scss-lint:disable IdSelector PlaceholderInExtend NestingDepth QualifyingElement SelectorDepth ImportantRule SelectorFormat
%font-awesome {
display: inline-block;
font-family: 'Font Awesome\ 5 Free';
margin-right: 5px;
}
%admin-form {
@extend .p-2;
@extend .mt-3;
background: $changeform-bg;
border: 1px solid $changeform-border-color;
clear: both;
overflow: auto;
h3 {
font-size: $h3-size;
}
.form-row {
// @extend .form-group; // @TODO check
background: $field-bg;
margin-bottom: .5rem;
padding: 1rem .5rem;
&.with-wrapped-fields {
background: transparent;
}
&.errors {
background: $field-row-error-bg;
.errorlist {
@extend .pt-2;
padding-left: 0;
width: 100%;
@include media-breakpoint-up(md) {
padding-left: 1.2rem;
}
li {
color: $danger-fg !important;
list-style-type: none;
font-weight: bold;
text-decoration: underline;
&::before {
@extend %font-awesome;
content: '\f12a';
display: inline-block;
margin-right: .5rem;
}
}
}
.wrapped-fields-container {
.errorlist {
padding-left: 0 !important;
}
}
}
.newline {
display: block;
height: .5rem;
width: 100%;
}
> div {
align-items: flex-start;
display: flex;
flex: 1;
flex-direction: row;
flex-wrap: wrap;
&.checkbox-row {
input {
margin-top: 5px;
order: 2;
}
label {
order: 1;
}
.help {
order: 3;
}
}
}
.date-icon {
&::before {
@extend %font-awesome;
content: '\f073';
}
}
.clock-icon {
&::before {
@extend %font-awesome;
content: '\f017';
}
}
.datetime {
align-items: flex-start;
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0;
input {
margin: 0 10px;
}
a + a {
@extend .me-3;
}
.datetimeshortcuts {
align-self: center;
white-space: nowrap;
}
.timezonewarning {
margin-left: 0 !important;
white-space: nowrap;
width: 0;
}
}
.datetimeshortcuts {
align-self: center;
+ .newline,
+ br {
content: '';
display: block;
height: .5rem;
width: 100%;
}
}
.timezonewarning {
@extend .py-0;
align-self: center;
color: $warning-fg;
display: block;
margin: .2rem 0;
padding-left: 1px;
@include media-breakpoint-up(md) {
margin-left: 20%;
}
@include media-breakpoint-up(lg) {
margin-left: 10%;
}
}
.related-widget-wrapper {
display: flex;
flex-direction: row;
flex-grow: 1;
+ p /* django 1.11 */ {
flex-basis: 100%;
margin-left: 0 !important;
@include media-breakpoint-up(md) {
margin-left: 20% !important;
}
@include media-breakpoint-up(lg) {
margin-left: 10% !important;
}
}
.add-related {
margin-left: 15px;
margin-right: 15px;
order: 2;
}
select {
display: inline-block !important;
}
}
// selector
.selector {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
&.stacked {
flex-direction: column;
.selector-chooser {
display: flex;
flex-direction: row;
margin: 15px 0;
width: auto;
.selector-add {
background: url('../img/selector-icons.svg') 0 -32px no-repeat; // 46
&.active {
background: url('../img/selector-icons.svg') 0 -48px no-repeat; // 46
}
}
.selector-remove {
background: url('../img/selector-icons.svg') 0 0 no-repeat; // 46
&.active {
background: url('../img/selector-icons.svg') 0 -16px no-repeat; // 46
}
}
}
}
.help-icon {
background: url('../img/icon-unknown.svg') 0 0 no-repeat;
cursor: help;
display: inline-block;
height: 13px;
margin: -2px 0 0 2px;
vertical-align: middle;
width: 13px;
}
}
.selector-filter {
align-items: center;
border-left: 1px solid $selector-item-border-color;
border-right: 1px solid $selector-item-border-color;
display: flex;
flex-direction: row;
margin-bottom: 0;
padding: 5px;
label {
margin-bottom: 0;
padding-right: 2px !important;
}
.search-label-icon {
background: url('../img/search.svg') 0 0 no-repeat;
cursor: help;
display: inline-block;
height: 18px;
margin-top: 5px;
width: 18px;
}
input {
padding: 5px;
}
}
.selector-available,
.selector-chosen {
text-align: center;
width: 330px;
h2 {
@extend .p-2;
background: $selector-available-title-bg;
border-left: 1px solid $selector-item-border-color;
border-right: 1px solid $selector-item-border-color;
border-top: 1px solid $selector-item-border-color;
font-size: 1rem;
margin-bottom: 0;
}
select {
border-radius: 0;
min-height: 200px;
width: 100%;
}
}
.selector-chosen {
h2 {
background: $selector-chosen-title-bg;
}
}
.selector-chooseall,
.selector-clearall {
@extend .btn;
@extend .btn-light;
display: inline-block;
margin: 5px auto;
&:not(.active) {
@extend .text-muted;
}
}
.selector-chooser {
align-self: center;
background-color: $selector-chooser-bg;
border-radius: 10px;
display: inline-block;
float: left;
margin: 0 15px;
padding: 0;
width: 22px;
li {
list-style-type: none;
margin: 0;
padding: 3px;
}
.selector-add {
background: url('../img/selector-icons.svg') 0 -112px no-repeat;
}
.selector-remove {
background: url('../img/selector-icons.svg') 0 -80px no-repeat;
}
.selector-add,
.selector-remove {
cursor: default;
display: block;
height: 16px;
opacity: .3;
overflow: hidden;
text-indent: -3000px;
width: 16px;
&.active {
opacity: 1;
}
}
}
.add-related,
.change-related {
vertical-align: top;
}
.change-related {
margin-left: 1rem;
}
.related-lookup {
&::before {
@extend %font-awesome;
content: '\f002';
margin-left: 5px;
}
}
}
label {
@extend .pe-3;
flex-basis: 100%;
text-align: left;
@include media-breakpoint-up(md) {
flex-basis: 20%;
text-align: right;
}
@include media-breakpoint-up(lg) {
flex-basis: 10%;
text-align: right;
}
}
.required {
font-weight: bold;
}
input[type=text],
input[type=password],
input[type=url],
input[type=number],
input[type=email],
input[type=date],
input[type=file],
select[multiple] {
@extend .form-control;
@include media-breakpoint-up(md) {
width: auto;
}
}
input[type=url] {
display: inline-block !important;
}
input[type=text][size] {
width: auto !important;
}
input[type=text]:not([size]) {
@include media-breakpoint-up(md) {
min-width: 240px;
}
}
textarea {
@extend .form-control;
resize: both;
@include media-breakpoint-up(md) {
width: auto;
}
}
.tabular.inline-related {
input[type=text],
input[type=number],
input[type=email],
input[type=date],
textarea,
select {
min-width: auto !important;
width: auto;
}
.inline-deletelink {
float: none !important;
}
}
select[multiple] {
display: inline-block;
}
select:not([multiple]):not(.admin-autocomplete) {
@extend .form-select;
text-indent: .01px;
text-overflow: clip;
@include media-breakpoint-up(md) {
width: auto;
}
}
// automcomplete
.select2-container--admin-autocomplete {
min-width: 320px; // m2m
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
height: 34px;
}
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
line-height: 36px;
}
.select2-container .select2-selection--single {
height: 36px;
}
.radiolist {
list-style-type: none;
padding-left: 0;
&.inline {
li {
display: inline-block;
}
}
}
.help {
@extend .form-text;
@extend .text-muted;
@extend .ms-3;
flex-basis: 100% !important;
margin-left: 0 !important;
margin-top: .5rem;
padding-left: 1px;
@include media-breakpoint-up(md) {
margin-left: 20% !important;
}
@include media-breakpoint-up(lg) {
margin-left: 10% !important;
}
> ul {
padding-left: 1.2rem;
}
}
div.help {
color: $help-text-color !important;
}
img.help {
display: inline-block;
margin-left: 0 !important;
}
.collapse {
display: block;
}
.collapsed * {
display: none;
}
.collapsed h2 {
display: block;
a {
display: inline;
}
}
.submit-row {
align-items: center;
display: flex;
flex-basis: baseline;
flex-direction: column;
margin-top: 15px;
@include media-breakpoint-up(md) {
flex-direction: row-reverse;
}
input[type=submit] {
@extend .btn;
@extend .btn-success;
@extend .ms-1;
margin-bottom: 15px;
margin-left: auto;
order: 1;
width: 200px;
@include media-breakpoint-up(md) {
margin-bottom: 0;
}
}
> a {
margin-bottom: 15px;
margin-left: .25rem;
order: 1;
@include media-breakpoint-up(md) {
margin-bottom: 0;
}
}
.deletelink-box {
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
order: 2;
text-align: center;
@include media-breakpoint-up(md) {
margin-left: 0;
}
.deletelink {
@extend .btn;
@extend .btn-danger;
width: 200px;
@include media-breakpoint-up(md) {
width: auto;
}
&::before {
@extend %font-awesome;
content: '\f1f8';
}
}
}
}
.errornote {
@extend .alert;
@extend .alert-danger;
font-weight: bold;
}
// fieldsets
fieldset .description {
@extend .pb-3;
@extend .pt-3;
}
fieldset.collapse {
display: block;
h2 {
align-items: center;
cursor: pointer;
display: flex;
flex-direction: row;
justify-content: space-between;
&::after {
color: $collapse-entry-icon-color;
content: '\f070';
display: inline-block;
font-family: 'Font Awesome\ 5 Free';
font-size: 1.1rem;
}
}
&.collapsed {
h2 {
&::after {
content: '\f06e';
}
}
}
}
// inline
fieldset .form-row {
margin-left: 0;
margin-right: 0;
}
fieldset > h2 {
background: $fieldset-title-bg;
font-size: $h2-size;
margin-bottom: 0;
padding: 5px 10px;
+ .form-row {
margin-top: 15px;
}
}
.inline-related {
overflow-x: auto;
&.empty-form, // stacked inline
.empty-form { // tabular
display: none !important; // inline extra param
}
textarea {
display: inline;
}
h3 {
@extend .clearfix;
@extend .mt-2;
align-items: center;
background: $inline-related-title-bg;
border-bottom: 1px solid $inline-related-title-border-color;
border-top: 5px solid $inline-related-title-border-top-color;
display: flex;
font-size: 1.1rem;
padding: 8px 10px;
.inline_label {
margin-left: .5rem;
margin-right: auto;
}
/* replaced by prev rules, keeping here for reference for a while
> .delete,
> .inline_label + span {
margin-left: auto;
}
*/
> .delete {
label {
margin-bottom: 0;
}
}
}
// datetime fields should not be blocks because are followed by the calendar icon
.vDateField {
display: inline-block;
}
.errorlist {
background: $field-row-error-bg;
}
> .module {
// @extend .table-responsive;
position: relative;
}
table {
@extend .table;
@extend .table-bordered;
th {
background: $inline-related-title-bg;
font-weight: normal !important;
&.required {
font-weight: bold !important;
}
.help-tooltip {
height: 15px;
margin-top: -3px;
width: 15px;
}
}
.form-row {
display: table-row;
}
.original {
border-right: 0px;
padding: 0;
position: relative;
width: 0;
p {
align-items: center;
background: $inline-tabular-original-bg;
border-bottom: 1px solid $inline-tabular-original-border-color;
display: flex;
font-size: .9rem;
flex-direction: row;
left: 14px;
padding: 0 4px;
position: absolute;
top: 8px;
white-space: nowrap;
.inlinechangelink {
position: relative;
right: -4px;
}
}
& + td,
& + th {
border-left: 0px;
}
}
.has_original td {
padding-top: 44px;
}
}
.inlinechangelink {
background: $inline-changelink-bg;
font-size: .9rem;
margin-left: 8px;
padding: 2px 4px;
&::before {
content: '\f304';
font-family: 'Font Awesome\ 5 Free';
margin-right: 4px;
}
&:hover {
background: darken($inline-changelink-bg, 10%);
text-decoration: none;
}
}
}
.add-row {
@extend .mt-2;
@extend .mb-2;
a {
@extend .btn;
@extend .btn-secondary;
&::before {
@extend %font-awesome;
content: '\f067';
}
}
}
.inline-deletelink {
@extend .btn;
@extend .btn-warning;
float: right;
margin-right: 15px;
white-space: nowrap;
&::before {
@extend %font-awesome;
content: '\f00d';
}
}
.wrapped-fields-container {
flex-direction: column !important;
.datetimeshortcuts {
align-self: top;
}
label {
flex-basis: auto !important;
}
@include media-breakpoint-up(md) {
flex-direction: row !important;
label {
flex-basis: 20% !important;
vertical-align: top;
}
}
@include media-breakpoint-up(lg) {
label {
flex-basis: 10% !important;
}
}
}
.wrapped-fields-container > * {
flex-grow: 0 !important;
}
.wrapped-fields-container .fieldBox {
margin-top: 1rem;
width: 100%;
label {
width: 100%;
}
.help {
margin-left: 0 !important;
}
@include media-breakpoint-up(md) {
margin-left: 1rem;
margin-top: 0;
width: auto;
label {
width: auto;
}
label + *:not(.datetime) {
display: inline-block;
}
}
}
}

View File

@@ -0,0 +1,172 @@
// bootstrap
$base: #d32d41;
$primary: darken($base, 20%);
$secondary: #b3c100;
$accent: #b3c100;
$font-family-sans-serif: 'Dosis', sans-serif !default;
// fonts
$h1-size: 1.6rem;
$h2-size: 1.4rem;
$h3-size: 1.2rem;
// utils
$transparent: transparent;
$warning-fg: darken(#ffc107, 10%);
$danger-fg: #dc3545;
// login
$login-bg: #fff;
$password-visibility-toggler-icon-fg: #999;
// navbar
$navbar-bg: #1c2429;
$navbar-link-color: #fff;
$navbar-dropdown-link-color: #000;
$navbar-link-color-hover: #d32d41;
$navbar-color: #fff;
$navbar-mobile-pane-bg: #263238;
// footer
$footer-bg: #1c2429;
$footer-link-color: $accent;
$footer-link-color-hover: darken($accent, 10%);
$footer-color: #fff;
$footer-border-color: #fff;
$footer-support-fg: #1c2429;
// menu
$menu-bg: #263238;
$menu-color: #fff;
$menu-branding-color: #fff;
$gravatar-border-color: #fff;
$user-links-fg: $accent;
$menu-link-color: #ccc;
$menu-link-hover-color: #fff;
$menu-li-hover-bg: darken($menu-bg, 2%);
$menu-li-border-color: #444;
$menu-active-voice-bg: darken($primary, 10%);
$menu-active-voice-border-color: #f5f5f5;
$menu-mobile-title-bg: #37474f;
$menu-mobile-title-color: #fff;
$menu-mobile-title-border-color: rgba(255, 255, 255, .2);
$menu-mobile-back-bg: darken($menu-bg, 5%);
$menu-selected-sub-bg: lighten($menu-bg, 5%);
$menu-title-bg: darken($menu-mobile-title-bg, 5%);
$menu-voice-title-color: $base;
$menu-voice-title-bg: darken($menu-bg, 5%);
$menu-active-bg: #f5f5f5;
$menu-branding-bg: darken($menu-bg, 5%);
// search field
$search-field-input-bg: lighten($menu-bg, 10%);
$search-field-input-border-color: $menu-bg;
$search-field-color: $menu-color;
$search-field-datalist-bg: darken($menu-bg, 5%);
$search-field-icon-color: lighten(#007eed, 25%);
$search-field-link-color: #fff;
$search-field-selected-bg: lighten($menu-bg, 20%);
$search-field-selected-color: #fff;
// dashboard
$dashboard-bg: #f5f5f5;
$dashboard-title-bg: #fafafa;
$dashboard-module-bg: #fff;
$dashboard-module-border-color: #fff;
$dashboard-caption-bg: #f6f6f6;
$dashboard-caption-color: #000;
$dashboard-icon-color: #f90;
$dashboard-delete-bg: #dc3545;
$dashboard-add-bg: #28a745;
$dashboard-change-bg: #007eed;
// breadcrumbs
$breadcrumbs-bg: #f0f0f0;
$breadcrumbs-border-color: #bbb;
// content
$content-bg: #f5f5f5;
// changelist
$changelist-bg: #fff;
$changelist-border-color: #fff;
$toolbar-bg: lighten($navbar-mobile-pane-bg, 75%);
$toolbar-btn-bg: #fff;
$toolbar-btn-hover-bg: #fafafa;
$sort-icon-color: #999;
$filter-toggler-color: #fff;
$filter-toggler-bg: #17a2b8;
$filters-bg: #fafafa;
$filters-title-bg: #263238;
$filters-title-color: #fff;
$filter-selected-border-color: $base;
$filters-clear-bg: #17a2b8;
$rl-head-bg: #f0f0f0;
$rl-head-color: inherit;
$rl-tr-odd-bg: #fafafa;
$rl-tr-odd-color: inherit;
$rl-tr-selected-bg-color: #cfc4a8;
$rl-tr-selected-bg: lighten($rl-tr-selected-bg-color, 10%);
$rl-tr-selected-color: inherit;
// changeform
$changeform-bg: #fff;
$changeform-border-color: #fff;
$collapse-entry-icon-color: #666;
$state-danger-bg: #f2dede;
$field-bg: #f9f9f9;
$field-row-error-bg: $state-danger-bg;
$fieldset-title-bg: lighten($base, 35%);
$inline-related-title-bg: #fafafa;
$inline-related-title-border-color: #eee;
$inline-related-title-border-top-color: #eee;
$inline-changelink-bg: #ffe4dc;
$inline-tabular-original-bg: #fafafa;
$inline-tabular-original-border-color: #eee;
$help-text-color: #007eed;
$clockbox-title-bg: #eee;
$clockbox-title-border-color: #ddd;
// changeform spinner
$spinner-overlay-bg: rgba(255, 255, 255, .8);
$spinner-overlay-fg: #000;
// form components
$selector-item-border-color: #ccc;
$selector-available-title-bg: #fafafa;
$selector-chosen-title-bg: lighten($base, 35%);
$selector-chooser-bg: #eee;
// changehistory
$changehistory-bg: #fff;
$changehistory-border-color: #fff;
// pagination
$pag-bg: #fff;
$pag-hover-bg: #eee;
$pag-border-color: #eee;
$pag-color: #000;
$pag-selected-bg: #455a64;
$pag-selected-color: #fff;
$pag-selected-border-color: #455a64;
// delete confirmation
$delete-confirmation-bg: #fff;
$delete-confirmation-border-color: #fff;
// analytics
$chart-bg: #fff;
$chart-border-color: #fff;
// modal
$modal-back-btn-bg: #007eed;
$modal-back-btn-fg: #fff;
// admindocs
$admindocs-module-bg: #fff;
$admindocs-module-header-bg: #f6f6f6;
$admindocs-module-header-border-color: #fff;
$admindocs-simple-bg: #ddd;
$admindocs-pre-bg: #333;
$admindocs-pre-color: #fff;
$admindocs-table-border-color: #e0e0e0;

View File

@@ -0,0 +1,28 @@
@import 'variables';
@import 'bootstrap/scss/bootstrap';
$fa-font-path: '../../node_modules/@fortawesome/fontawesome-free/webfonts';
@import '../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
@import '../../node_modules/@fortawesome/fontawesome-free/scss/solid';
@import '../../node_modules/@fortawesome/fontawesome-free/scss/brands';
@import 'fonts';
@import 'placeholders';
@import 'main';
@import 'login';
@import 'logout';
@import 'navbar';
@import 'footer';
@import 'menu';
@import 'dashboard';
@import 'breadcrumbs';
@import 'content';
@import 'changelist';
@import 'changeform';
@import 'changehistory';
@import 'pagination';
@import 'delete_confirmation';
@import 'passwordchange';
@import 'actionresult';
@import 'calendarclock';
@import 'analytics';
@import 'admindocs';
@import 'filer';

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,51 @@
const webpack = require("webpack");
const path = require("path");
module.exports = {
resolve: {
// always import from root (src and node_modules)
modules: [path.join(__dirname, "src"), "node_modules"],
extensions: [".js"],
},
// library entry point
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
publicPath: "/static/baton/app/dist/",
filename: "baton.min.js",
clean: true,
},
plugins: [
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery",
}),
new webpack.DefinePlugin({
BATON_REVISION: JSON.stringify(process.env.BATON_REVISION),
}),
],
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
{
test: /\.scss/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
},
{
test: /\.sass/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
},
{
test: /\.(svg|eot|woff|woff2|ttf|png|jpe?g|gif)(\?\S*)?$/,
type: "asset/resource",
},
],
},
};

View File

@@ -0,0 +1,20 @@
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
module.exports = merge(common, {
mode: "development",
output: {
...common.output,
publicPath: "http://localhost:8080/static/baton/app/dist/",
},
plugins: [...common.plugins, new BundleAnalyzerPlugin()],
// this for creating source maps
devtool: "eval-source-map",
devServer: {
headers: {
"Access-Control-Allow-Origin": "*", // allow CORS on fonts
},
},
});

View File

@@ -0,0 +1,7 @@
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'production',
devtool: false,
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB