Merge pull request #1 from Thibaut/master

Pull all
pull/620/head
彭伟聪 8 years ago committed by GitHub
commit e24c7502a1

@ -1 +1 @@
2.4.0
2.4.1

@ -1,5 +1,5 @@
FROM ruby:2.4.0
FROM ruby:2.4.1
MAINTAINER Conor Heine <conor.heine@gmail.com>
RUN apt-get update

@ -1,5 +1,5 @@
source 'https://rubygems.org'
ruby '2.4.0'
ruby '2.4.1'
gem 'rake'
gem 'thor'

@ -6,7 +6,7 @@ GEM
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
backports (3.6.8)
backports (3.7.0)
better_errors (2.1.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
@ -35,7 +35,7 @@ GEM
mini_portile2 (2.1.0)
minitest (5.10.1)
multi_json (1.12.1)
nokogiri (1.7.0.1)
nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
options (2.3.2)
progress_bar (1.1.0)
@ -76,18 +76,18 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
tilt (2.0.6)
tty-pager (0.5.0)
tilt (2.0.7)
tty-pager (0.7.1)
tty-screen (~> 0.5.0)
tty-which (~> 0.2.0)
tty-which (~> 0.3.0)
verse (~> 0.5.0)
tty-screen (0.5.0)
tty-which (0.2.2)
tty-which (0.3.0)
typhoeus (1.1.2)
ethon (>= 0.9.0)
tzinfo (1.2.2)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uglifier (3.1.3)
uglifier (3.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.1.3)
unicode_utils (1.4.0)
@ -129,7 +129,7 @@ DEPENDENCIES
yajl-ruby
RUBY VERSION
ruby 2.4.0p0
ruby 2.4.1p111
BUNDLED WITH
1.14.1

@ -20,7 +20,7 @@ Unless you wish to contribute to the project, I recommend using the hosted versi
DevDocs is made of two pieces: a Ruby scraper that generates the documentation and metadata, and a JavaScript app powered by a small Sinatra app.
DevDocs requires Ruby 2.4.0, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands:
DevDocs requires Ruby 2.4.1, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands:
```
git clone https://github.com/Thibaut/devdocs.git && cd devdocs

@ -92,8 +92,6 @@
for doc in @DOCS
(if docs.indexOf(doc.slug) >= 0 then @docs else @disabledDocs).add(doc)
@migrateDocs()
@docs.sort()
@disabledDocs.sort()
@docs.load @start.bind(@), @onBootError.bind(@), readCache: true, writeCache: true
delete @DOCS
return

@ -34,19 +34,24 @@ class app.Router
return
before: (context, next) ->
previousContext = @context
@context = context
@trigger 'before', context
next()
return
if res = next()
@context = previousContext
return res
else
return
doc: (context, next) ->
if doc = app.docs.findBySlug(context.params.doc) or app.disabledDocs.findBySlug(context.params.doc)
context.doc = doc
context.entry = doc.toEntry()
@triggerRoute 'entry'
return
else
next()
return
return next()
type: (context, next) ->
doc = app.docs.findBySlug(context.params.doc)
@ -55,9 +60,9 @@ class app.Router
context.doc = doc
context.type = type
@triggerRoute 'type'
return
else
next()
return
return next()
entry: (context, next) ->
doc = app.docs.findBySlug(context.params.doc)
@ -66,36 +71,39 @@ class app.Router
context.doc = doc
context.entry = entry
@triggerRoute 'entry'
return
else
next()
return
return next()
root: ->
if app.isSingleDoc()
setTimeout (-> window.location = '/'), 0
else
@triggerRoute 'root'
return '/' if app.isSingleDoc()
@triggerRoute 'root'
return
settings: ->
settings: (context) ->
return "/#/#{context.path}" if app.isSingleDoc()
@triggerRoute 'settings'
return
offline: ->
offline: (context)->
return "/#/#{context.path}" if app.isSingleDoc()
@triggerRoute 'offline'
return
about: (context) ->
return "/#/#{context.path}" if app.isSingleDoc()
context.page = 'about'
@triggerRoute 'page'
return
news: (context) ->
return "/#/#{context.path}" if app.isSingleDoc()
context.page = 'news'
@triggerRoute 'page'
return
help: (context) ->
return "/#/#{context.path}" if app.isSingleDoc()
context.page = 'help'
@triggerRoute 'page'
return
@ -104,18 +112,15 @@ class app.Router
@triggerRoute 'notFound'
return
isRoot: ->
location.pathname is '/'
isDocIndex: ->
@context and @context.doc and @context.entry is @context.doc.toEntry()
isIndex: ->
@context.path is '/' or (app.isSingleDoc() and @context?.entry?.isIndex())
setInitialPath: ->
# Remove superfluous forward slashes at the beginning of the path
if (path = location.pathname.replace /^\/{2,}/g, '/') isnt location.pathname
page.replace path + location.search + location.hash, null, true
if @isRoot()
if location.pathname is '/'
if path = @getInitialPathFromHash()
page.replace path + location.search, null, true
else if path = @getInitialPathFromCookie()

@ -57,8 +57,9 @@ class app.Shortcuts
@trigger 'enter'
when 27
@trigger 'escape'
false
when 32
if not @lastKeypress or @lastKeypress < Date.now() - 500
if event.target.type is 'search' and (not @lastKeypress or @lastKeypress < Date.now() - 500)
@trigger 'pageDown'
false
when 33

@ -4,11 +4,19 @@ class app.collections.Docs extends app.Collection
findBySlug: (slug) ->
@findBy('slug', slug) or @findBy('slug_without_version', slug)
NORMALIZE_VERSION_RGX = /\.(\d)$/
NORMALIZE_VERSION_SUB = '.0$1'
sort: ->
@models.sort (a, b) ->
a = a.name.toLowerCase()
b = b.name.toLowerCase()
if a < b then -1 else if a > b then 1 else 0
if a.name is b.name
if not a.version or a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB)
-1
else
1
else if a.name.toLowerCase() > b.name.toLowerCase()
1
else
-1
# Load models concurrently.
# It's not pretty but I didn't want to import a promise library only for this.

@ -38,11 +38,15 @@ page.stop = ->
page.show = (path, state) ->
return if path is currentState?.path
context = new Context(path, state)
previousState = currentState
currentState = context.state
page.dispatch(context)
context.pushState()
updateCanonicalLink()
track()
if res = page.dispatch(context)
currentState = previousState
location.assign(res)
else
context.pushState()
updateCanonicalLink()
track()
context
page.replace = (path, state, skipDispatch, init) ->
@ -58,10 +62,9 @@ page.replace = (path, state, skipDispatch, init) ->
page.dispatch = (context) ->
i = 0
next = ->
fn(context, next) if fn = callbacks[i++]
return
next()
return
res = fn(context, next) if fn = callbacks[i++]
return res
return next()
page.canGoBack = ->
not Context.isIntialState(currentState)
@ -116,10 +119,9 @@ class Route
(context, next) =>
if @match context.pathname, params = []
context.params = params
fn(context, next)
return fn(context, next)
else
next()
return
return next()
match: (path, params) ->
return unless matchData = @regexp.exec(path)

@ -167,7 +167,8 @@ $.scrollTo = (el, parent, position = 'center', options = {}) ->
return unless parent
parentHeight = parent.clientHeight
return unless parent.scrollHeight > parentHeight
parentScrollHeight = parent.scrollHeight
return unless parentScrollHeight > parentHeight
top = $.offset(el, parent).top
offsetTop = parent.firstElementChild.offsetTop
@ -181,14 +182,17 @@ $.scrollTo = (el, parent, position = 'center', options = {}) ->
scrollTop = parent.scrollTop
height = el.offsetHeight
lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight
offsetBottom = if lastElementOffset > 0 then parentScrollHeight - lastElementOffset else 0
# If the target element is above the visible portion of its scrollable
# ancestor, move it near the top with a gap = options.topGap * target's height.
if top - offsetTop <= scrollTop + height * (options.topGap or 1)
parent.scrollTop = top - offsetTop - height * (options.topGap or 1)
# If the target element is below the visible portion of its scrollable
# ancestor, move it near the bottom with a gap = options.bottomGap * target's height.
else if top >= scrollTop + parentHeight - height * ((options.bottomGap or 1) + 1)
parent.scrollTop = top - parentHeight + height * ((options.bottomGap or 1) + 1)
else if top + offsetBottom >= scrollTop + parentHeight - height * ((options.bottomGap or 1) + 1)
parent.scrollTop = top + offsetBottom - parentHeight + height * ((options.bottomGap or 1) + 1)
return
$.scrollToWithImageLock = (el, parent, args...) ->
@ -329,11 +333,11 @@ $.requestAnimationFrame = (fn) ->
$.noop = ->
$.popup = (value) ->
win = window.open()
if win
try
win = window.open()
win.opener = null if win.opener
win.location = value.href or value
else
catch
window.open value.href or value, '_blank'
return

@ -6,13 +6,13 @@ app.templates.settingsPage = (settings) -> """
<div class="_settings-inputs">
<label class="_settings-label">
<input type="checkbox" name="dark" value="1"#{if settings.dark then ' checked' else ''}>Enable dark theme
<input type="checkbox" form="settings" name="dark" value="1"#{if settings.dark then ' checked' else ''}>Enable dark theme
</label>
<label class="_settings-label _setting-max-width">
<input type="checkbox" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout
<input type="checkbox" form="settings" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout
</label>
<label class="_settings-label _hide-on-mobile">
<input type="checkbox" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
<input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
<small>Tip: drag the edge of the sidebar to resize it.</small>
</label>
</div>
@ -23,10 +23,10 @@ app.templates.settingsPage = (settings) -> """
<div class="_settings-inputs">
<label class="_settings-label">
<input type="checkbox" name="smoothScroll" value="1"#{if settings.smoothScroll then ' checked' else ''}>Use smooth scrolling
<input type="checkbox" form="settings" name="smoothScroll" value="1"#{if settings.smoothScroll then ' checked' else ''}>Use smooth scrolling
</label>
<label class="_settings-label">
<input type="checkbox" name="arrowScroll" value="1"#{if settings.arrowScroll then ' checked' else ''}>Use arrow keys to scroll the main content area
<input type="checkbox" form="settings" name="arrowScroll" value="1"#{if settings.arrowScroll then ' checked' else ''}>Use arrow keys to scroll the main content area
<small>With this checked, use <code class="_label">alt</code> + <code class="_label">&uarr;</code><code class="_label">&darr;</code><code class="_label">&larr;</code><code class="_label">&rarr;</code> to navigate the sidebar.</small>
</label>
</div>

@ -1,4 +1,4 @@
/*! Raven.js 3.13.1 (f55d281) | github.com/getsentry/raven-js */
/*! Raven.js 3.14.2 (5cf57e1) | github.com/getsentry/raven-js */
/*
* Includes TraceKit
@ -115,6 +115,9 @@ function Raven() {
crossOrigin: 'anonymous',
collectWindowErrors: true,
maxMessageLength: 0,
// By default, truncates URL values to 250 chars
maxUrlLength: 250,
stackTraceLimit: 50,
autoBreadcrumbs: true,
sampleRate: 1
@ -152,7 +155,7 @@ Raven.prototype = {
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
VERSION: '3.13.1',
VERSION: '3.14.2',
debug: false,
@ -457,9 +460,10 @@ Raven.prototype = {
if (this._globalOptions.stacktrace || (options && options.stacktrace)) {
var ex;
// create a stack trace from this point; just trim
// off extra frames so they don't include this function call (or
// earlier Raven.js library fn calls)
// Generate a "synthetic" stack trace from this point.
// NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative
// of a bug with Raven.js. Sentry generates synthetic traces either by configuration,
// or if it catches a thrown object without a "stack" property.
try {
throw new Error(msg);
} catch (ex1) {
@ -473,6 +477,9 @@ Raven.prototype = {
// fingerprint on msg, not stack trace (legacy behavior, could be
// revisited)
fingerprint: msg,
// since we know this is a synthetic trace, the top N-most frames
// MUST be from Raven.js, so mark them as in_app later by setting
// trimHeadFrames
trimHeadFrames: (options.trimHeadFrames || 0) + 1
}, options);
@ -1385,9 +1392,48 @@ Raven.prototype = {
exception.value = truncate(exception.value, max);
}
var request = data.request;
if (request) {
if (request.url) {
request.url = truncate(request.url, this._globalOptions.maxUrlLength);
}
if (request.Referer) {
request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength);
}
}
if (data.breadcrumbs && data.breadcrumbs.values)
this._trimBreadcrumbs(data.breadcrumbs);
return data;
},
/**
* Truncate breadcrumb values (right now just URLs)
*/
_trimBreadcrumbs: function (breadcrumbs) {
// known breadcrumb properties with urls
// TODO: also consider arbitrary prop values that start with (https?)?://
var urlProps = ['to', 'from', 'url'],
urlProp,
crumb,
data;
for (var i = 0; i < breadcrumbs.values.length; ++i) {
crumb = breadcrumbs.values[i];
if (!crumb.hasOwnProperty('data') || !isObject(crumb.data))
continue;
data = crumb.data;
for (var j = 0; j < urlProps.length; ++j) {
urlProp = urlProps[j];
if (data.hasOwnProperty(urlProp)) {
data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength);
}
}
}
},
_getHttpData: function() {
if (!this._hasNavigator && !this._hasDocument) return;
var httpData = {};
@ -2083,20 +2129,22 @@ function isObject(what) {
return typeof what === 'object' && what !== null;
}
// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
// Yanked from https://git.io/vS8DV re-used under CC0
// with some tiny modifications
function isError(what) {
var toString = {}.toString.call(what);
return isObject(what) &&
toString === '[object Error]' ||
toString === '[object Exception]' || // Firefox NS_ERROR_FAILURE Exceptions
what instanceof Error;
function isError(value) {
switch ({}.toString.call(value)) {
case '[object Error]': return true;
case '[object Exception]': return true;
case '[object DOMException]': return true;
default: return value instanceof Error;
}
}
module.exports = {
isObject: isObject,
isError: isError
};
},{}],6:[function(_dereq_,module,exports){
(function (global){
'use strict';
@ -2424,27 +2472,6 @@ TraceKit.report = (function reportModuleWrapper() {
*
*/
TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
/**
* Escapes special characters, except for whitespace, in a string to be
* used inside a regular expression as a string literal.
* @param {string} text The string.
* @return {string} The escaped string literal.
*/
function escapeRegExp(text) {
return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&');
}
/**
* Escapes special characters in a string to be used inside a regular
* expression as a string literal. Also ensures that HTML entities will
* be matched the same as their literal friends.
* @param {string} body The string.
* @return {string} The escaped string.
*/
function escapeCodeAsRegExpForMatchingInsideHTML(body) {
return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('"', '(?:"|&quot;)').replace(/\s+/g, '\\s+');
}
// Contents of Exception in various browsers.
//
// SAFARI:
@ -2491,18 +2518,31 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
function computeStackTraceFromStackProp(ex) {
if (typeof ex.stack === 'undefined' || !ex.stack) return;
var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|<anonymous>).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|resource|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i,
winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i,
winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
// Used to additionally parse URL/line/column from eval frames
geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i,
chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/,
lines = ex.stack.split('\n'),
stack = [],
submatch,
parts,
element,
reference = /^(.*) is undefined$/.exec(ex.message);
for (var i = 0, j = lines.length; i < j; ++i) {
if ((parts = chrome.exec(lines[i]))) {
var isNative = parts[2] && parts[2].indexOf('native') !== -1;
var isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line
var isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
if (isEval && (submatch = chromeEval.exec(parts[2]))) {
// throw out eval line/column and use top-most line/column number
parts[2] = submatch[1]; // url
parts[3] = submatch[2]; // line
parts[4] = submatch[3]; // column
}
element = {
'url': !isNative ? parts[2] : null,
'func': parts[1] || UNKNOWN_FUNCTION,
@ -2519,6 +2559,19 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
'column': parts[4] ? +parts[4] : null
};
} else if ((parts = gecko.exec(lines[i]))) {
var isEval = parts[3] && parts[3].indexOf(' > eval') > -1;
if (isEval && (submatch = geckoEval.exec(parts[3]))) {
// throw out eval line/column and use top-most line number
parts[3] = submatch[1];
parts[4] = submatch[2];
parts[5] = null; // no column when eval
} else if (i === 0 && !parts[5] && typeof ex.columnNumber !== 'undefined') {
// FireFox uses this awesome columnNumber property for its top frame
// Also note, Firefox's column number is 0-based and everything else expects 1-based,
// so adding 1
// NOTE: this hack doesn't work if top-most frame is eval
stack[0].column = ex.columnNumber + 1;
}
element = {
'url': parts[3],
'func': parts[1] || UNKNOWN_FUNCTION,
@ -2541,13 +2594,6 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
return null;
}
if (!stack[0].column && typeof ex.columnNumber !== 'undefined') {
// FireFox uses this awesome columnNumber property for its top frame
// Also note, Firefox's column number is 0-based and everything else expects 1-based,
// so adding 1
stack[0].column = ex.columnNumber + 1;
}
return {
'name': ex.name,
'message': ex.message,

@ -45,11 +45,8 @@ class app.views.OfflinePage extends app.View
docEl: (doc) ->
@find("[data-slug='#{doc.slug}']")
onRoute: (route) ->
if app.isSingleDoc()
window.location = "/#/#{route.path}"
else
@render()
onRoute: (context) ->
@render()
return
onClick: (event) =>

@ -58,9 +58,6 @@ class app.views.SettingsPage extends app.View
@toggle input.name, input.checked
return
onRoute: (route) =>
if app.isSingleDoc()
window.location = "/#/#{route.path}"
else
@render()
onRoute: (context) ->
@render()
return

@ -11,7 +11,6 @@ class app.views.Settings extends app.View
@events:
submit: 'onSubmit'
click: 'onClick'
focus: 'onFocus'
@shortcuts:
enter: 'onEnter'
@ -69,10 +68,6 @@ class app.views.Settings extends app.View
app.router.show '/'
return
onFocus: (event) =>
$.scrollTo event.target, @el, 'continuous', bottomGap: 2
return
onAppCacheProgress: (event) =>
if event.lengthComputable
percentage = Math.round event.loaded * 100 / event.total

@ -19,7 +19,6 @@ class app.views.Search extends app.View
altS: 'stackoverflow'
@routes:
root: 'onRoot'
after: 'afterRoute'
init: ->
@ -42,7 +41,7 @@ class app.views.Search extends app.View
autoFocus: =>
unless app.isMobile() or $.isAndroid() or $.isIOS()
@input.focus() unless document.activeElement is @input
@input.focus() unless document.activeElement?.tagName is 'INPUT'
return
reset: ->
@ -77,9 +76,9 @@ class app.views.Search extends app.View
return
searchUrl: =>
if app.router.isRoot()
if location.pathname is '/'
@scope.searchUrl()
else if not app.router.isDocIndex()
else if not app.router.isIndex()
return
return unless value = @extractHashValue()
@ -129,11 +128,8 @@ class app.views.Search extends app.View
$.stopEvent(event)
return
onRoot: (context) =>
@reset() unless context.init
return
afterRoute: (name, context) =>
@reset() if not context.init and app.router.isIndex()
@delay @searchUrl if context.hash
@delay @autoFocus
return

@ -70,7 +70,7 @@ class app.views.SearchScope extends app.View
redirectToDoc: (doc) ->
hash = location.hash
app.router.replaceHash('')
window.location = doc.fullPath() + hash
location.assign doc.fullPath() + hash
return
reset: =>

@ -3,6 +3,7 @@ class app.views.DocPicker extends app.View
@events:
mousedown: 'onMouseDown'
mouseup: 'onMouseUp'
init: ->
@addSubview @listFold = new app.views.ListFold(@el)
@ -64,13 +65,18 @@ class app.views.DocPicker extends app.View
@mouseDown = Date.now()
return
onMouseUp: =>
@mouseUp = Date.now()
return
onDOMFocus: (event) =>
target = event.target
if target.tagName is 'INPUT'
$.scrollTo target.parentNode, null, 'continuous', bottomGap: 2
unless (@mouseDown and Date.now() < @mouseDown + 100) or (@mouseUp and Date.now() < @mouseUp + 100)
$.scrollTo target.parentNode, null, 'continuous'
else if target.classList.contains(app.views.ListFold.targetClass)
target.blur()
unless @mouseDown and @mouseDown > Date.now() - 100
unless @mouseDown and Date.now() < @mouseDown + 100
if @focusEl is $('input', target.nextElementSibling)
@listFold.close(target) if target.classList.contains(app.views.ListFold.activeClass)
prev = target.previousElementSibling

@ -46,7 +46,7 @@ class app.views.Results extends app.View
return
openFirst: ->
@el.firstElementChild?.click()
setTimeout (=> @el.firstElementChild?.click()), 0
return
onDocEnabled: (doc) ->

@ -12,13 +12,13 @@
&._in { display: block; }
._sidebar {
display: block !important;
&:after { // padding bottom
content: '';
display: block;
height: $headerHeight;
}
._sidebar-hidden & { display: block; }
}
._header { justify-content: space-between; }

@ -47,12 +47,11 @@
<button type="button" class="_settings-tab active" data-tab="doc-picker" hidden>Docs</button><button type="button" class="_settings-tab" data-tab="settings" hidden>Settings</button>
</nav>
</div>
<div class="_sidebar">
<div class="_sidebar-footer">
<button type="submit" class="_settings-btn">
<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></svg> Apply
</button>
</div>
<div class="_sidebar" tabindex="-1"></div>
<div class="_sidebar-footer">
<button type="submit" class="_settings-btn">
<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></svg> Apply
</button>
</div>
</form>
</div>

Loading…
Cancel
Save