head 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fed25e922ef5971cee84f0a126455fc7905b22cad95e682bc7d4ec9d73e45a67
4
- data.tar.gz: 92b16e33d79efbc6f61a8eed32e1ddacdae0895c87b275b6e2639df3ab5786c4
3
+ metadata.gz: 64555f34c0083982a934eab9077ed59c080d25e5c30d649c45bb72f91d5a4d7d
4
+ data.tar.gz: 7d56527224c27361e457345eabeea6fedacda3d820608f50bd0e2afd2463f59a
5
5
  SHA512:
6
- metadata.gz: c34f9d1f4238f5bf21a93f278e39aebce14701ba695c3a14d8992c939249cd212259993b2483940c8a89de7ff9d0b79e1c64ad777eaa3f2c9d4f4bb00eea2d9e
7
- data.tar.gz: 1ba05cbd6fb2cf090759eee12f0a782ad232c3dfe70c40b00bee4b5bacf6cecc8045e1fdf1ef62eabbab276020f17b5cb3fd25568390206fa2b616e01a7593ca
6
+ metadata.gz: d755be54df9724b2c9dea550f04bb10d3c211ea6d06028eb6dedee133f603ab80a614515f2e038a651ff6cd5c83c18598bbb14098b7f0032f7e5d52c61606629
7
+ data.tar.gz: 23c1c19da4691d95d45411ae3c13c15ddc847d0dfa9767d415569c2e994119015dcbe9e3daf47979d4340c1f9feaf33fdd57a377df5b5ad7238cc9a72f3402e3
@@ -1,2 +1,3 @@
1
1
  import "head/knob"
2
2
  import "head/wing"
3
+ import "head/sidebar"
@@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', function () {
14
14
 
15
15
  // Check screen size
16
16
  const mainMenuWing = document.querySelector(`.js-head-wing[data-identifier="mainmenu"]`)
17
- const tight = getComputedStyle(mainMenuWing).getPropertyValue('position') == 'absolute'
17
+ const tight = mainMenuWing ? getComputedStyle(mainMenuWing).getPropertyValue('position') === 'absolute' : false
18
18
 
19
19
  // Handle current wing
20
20
  const wing = document.querySelector(`.js-head-wing[data-identifier="${identifier}"]`)
@@ -0,0 +1,39 @@
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ document.querySelectorAll('.js-head-sidebar__category').forEach(function (category) {
3
+ console.debug("Binding to Sidebar Category")
4
+ const href = category.getAttribute('href')
5
+
6
+ // If this category doesn't open a secondary sidebar, it is a standalone link.
7
+ if (href != '#') return
8
+
9
+ category.addEventListener('click', function(event) {
10
+ event.preventDefault()
11
+ const category = event.currentTarget
12
+
13
+ // The identifier of a category always matches that of its links list.
14
+ const identifier = category.dataset.identifier
15
+ const active = category.classList.contains('is-active')
16
+ const links = document.querySelector(`.js-head-sidebar__links[data-identifier="${identifier}"]`)
17
+
18
+ // Close all others
19
+ document.querySelectorAll('.js-head-sidebar__links').forEach((someLinks) => {
20
+ if (links == someLinks) return
21
+
22
+ someLinks.classList.remove('is-active')
23
+ const categoryClass = `.js-head-sidebar__category[data-identifier="${someLinks.dataset.identifier}"]`
24
+ document.querySelectorAll(categoryClass).forEach((someCategory) => {
25
+ someCategory.classList.remove('is-active')
26
+ })
27
+ })
28
+
29
+ // Now toggle the desired one.
30
+ if (active) {
31
+ category.classList.remove('is-active')
32
+ links.classList.remove('is-active')
33
+ } else {
34
+ category.classList.add('is-active')
35
+ links.classList.add('is-active')
36
+ }
37
+ })
38
+ })
39
+ })
@@ -1,3 +1,4 @@
1
+ @use "iglu/responsive"
1
2
  @use "iglu/font-size"
2
3
  @use "iglu/spacing"
3
4
  @use "head/settings/colors"
@@ -9,7 +10,7 @@
9
10
  padding-right: 0.25em
10
11
  text-decoration: none
11
12
  +spacing.padding-vertical--tiny
12
- +font-size.huge
13
+ +font-size.gigantic
13
14
 
14
15
  +link.states
15
16
  color: colors.$steel-gray
@@ -20,6 +21,9 @@
20
21
  &:active
21
22
  color: colors.$white
22
23
 
24
+ &--mainmenu
25
+ display: grid
26
+
23
27
  &--identicon
24
28
  max-width: 1em
25
29
  aspect-ratio: 1
@@ -48,3 +52,12 @@
48
52
  object-fit: contain
49
53
  clip-path: circle()
50
54
  background-color: #fff
55
+
56
+
57
+ // Keep this breakpoint analogous to wing.sass
58
+ +responsive.xlarge
59
+ .c-head-knob
60
+
61
+ &--mainmenu
62
+ display: none
63
+
@@ -0,0 +1,99 @@
1
+ @use "iglu/spacing"
2
+ @use "iglu/font-size"
3
+ @use "head/settings/colors"
4
+ @use "head/tools/link"
5
+
6
+ .c-head-sidebar
7
+ display: grid
8
+ grid-template-columns: auto 1fr
9
+ height: 100%
10
+ align-items: start
11
+ +font-size.default
12
+
13
+ &__categories
14
+ display: grid
15
+
16
+ &--right
17
+ .c-head-sidebar__categories
18
+ order: 2
19
+
20
+ &__secondary
21
+ overflow: hidden
22
+ background-color: rgba(255, 255, 255, 0.14) // Corrsponds to 10% change in underlying color
23
+ height: 100%
24
+
25
+ &__category
26
+ display: grid
27
+ justify-content: center
28
+ grid-template-areas: "icon" "name"
29
+ padding-top: 0.2em
30
+ padding-bottom: 0.2em
31
+ position: relative
32
+ text-decoration: none
33
+ +spacing.padding-horizontal--tiny
34
+
35
+ @each $column in category-icon category-name
36
+ &__#{$column}
37
+ grid-area: #{$column}
38
+
39
+ +link.states
40
+ color: colors.$moon-gray
41
+
42
+ &:hover
43
+ color: colors.$white
44
+
45
+ &.is-active
46
+ color: colors.$white
47
+ background-color: rgba(255, 255, 255, 0.14) // Corrsponds to 10% change in underlying color
48
+
49
+ &-icon
50
+ text-align: center
51
+ +font-size.huge
52
+
53
+ &-name
54
+ +font-size.smaller
55
+
56
+ &__links
57
+ display: none
58
+
59
+ &.is-active
60
+ display: block
61
+
62
+ &__link
63
+ grid-column-gap: 0.6rem
64
+ display: grid
65
+ align-items: center
66
+ grid-template-columns: auto 1fr
67
+ grid-template-areas: "icon name"
68
+ padding-right: 0.5rem
69
+ padding-left: 0.5rem
70
+ text-decoration: none
71
+ height: 100%
72
+ +spacing.padding-vertical--tiny
73
+
74
+ @each $column in link-icon link-name
75
+ &__#{$column}
76
+ grid-area: #{$column}
77
+
78
+ +link.states
79
+ color: colors.$moon-gray
80
+
81
+ &:hover
82
+ color: colors.$white
83
+
84
+ &:active
85
+ color: colors.$white
86
+
87
+ &.is-active
88
+ color: colors.$white
89
+ background-color: rgba(255, 255, 255, 0.14)
90
+
91
+ &-name
92
+ white-space: nowrap
93
+ +font-size.small
94
+
95
+ &-icon
96
+ +font-size.larger
97
+
98
+ &-separator
99
+ +spacing.margin-top--smaller
@@ -8,11 +8,12 @@
8
8
  grid-template-rows: auto 1fr
9
9
 
10
10
  &__roof
11
- background-color: #444
11
+ background-color: colors.$granite-gray
12
12
  display: grid
13
13
  line-height: 1 // Conformity of knobs
14
14
  align-items: center
15
- grid-template-columns: auto 1fr auto
15
+ // Keep Logo centered even if left and right menus differ in width
16
+ grid-template-columns: 1fr 1fr 1fr
16
17
  grid-template-areas: "knobs--left logo knobs--right"
17
18
 
18
19
  @each $column in knobs--left logo knobs--right
@@ -4,8 +4,9 @@
4
4
  .c-head-wing
5
5
  display: none
6
6
  // 320px is the min screen width we support.
7
- // Our sidebar cannot be bigger, and it needs to leave some space
8
- // at the right so that a click/tap there can close the sidebar.
7
+ // On devices that are that narrow, the sidebar needs to fit in,
8
+ // as well as a blank area on the right to close the sidebar with a tap.
9
+ // So the sidebar is limited to less than 320px on narrow devices.
9
10
  max-width: 265px
10
11
  height: 100%
11
12
  z-index: 1000
@@ -13,22 +14,12 @@
13
14
  background: colors.$granite-gray
14
15
  color: colors.$white
15
16
 
16
- &--left
17
- // On rather large screens, always show the main menu in left sidebar
18
- +responsive.xlarge-container
19
- position: static
20
-
21
- &[data-identifier="mainmenu"]
22
- // We "force" this item to be active without having the `is-active` class.
23
- display: block
24
- // So we need some way to deactivate it again using JS.
25
- &.is-force-deactive
26
- display: none
27
-
28
17
  &--right
29
18
  right: 0
19
+ position: inherit
30
20
 
31
- // On very very large screens, show notifications sidebar on the right.
21
+ // On very very large screens, permanently show notifications sidebar on the right.
22
+ // Using container queries for this, because the main menu on the left may affect the layout.
32
23
  +responsive.xxlarge-container
33
24
  position: static
34
25
 
@@ -57,14 +48,19 @@
57
48
  +responsive.xlarge-container
58
49
  display: none
59
50
 
51
+ // Keep this breakpoint analogous to knob.sass
52
+ +responsive.xlarge
53
+ .c-head-wing
60
54
 
61
- // +responsive.xxlarge
62
- // .c-head-wing
63
-
64
- // // We "force" this item to be active without having the `is-active` class.
65
- // &[data-identifier="mainmenu"]
66
- // display: block
55
+ &--left
56
+ // On rather large screens, always show the main menu in left sidebar
57
+ position: static
58
+ // On wide screens, it's ok to have a wider sidebar to fit all text.
59
+ max-width: 350px
67
60
 
68
- // // So we need some way to deactivate it again using JS.
69
- // &.is-force-deactive
70
- // display: none
61
+ &[data-identifier="mainmenu"]
62
+ // We "force" this item to be active without having the `is-active` class.
63
+ display: block
64
+ // // So we need some way to deactivate it again using JS.
65
+ // &.is-force-deactive
66
+ // display: none
@@ -2,5 +2,5 @@
2
2
  font-family: 'headicons'
3
3
  font-weight: normal
4
4
  font-style: normal
5
- src: url('head/headicons.woff') format('woff')
5
+ src: url('/head/headicons.woff') format('woff')
6
6
 
@@ -3,6 +3,7 @@
3
3
  @use "./generics/flip"
4
4
  @use "./generics/fonts"
5
5
 
6
+ @use "./components/sidebar"
6
7
  @use "./components/theater"
7
8
  @use "./components/knob"
8
9
  @use "./components/wing"
@@ -27,6 +27,14 @@
27
27
  &:before
28
28
  content: headicons.$bell
29
29
 
30
+ .o-headicon--bell-ringing
31
+ &:before
32
+ content: headicons.$bell-ringing
33
+
30
34
  .o-headicon--gear
31
35
  &:before
32
36
  content: headicons.$gear
37
+
38
+ .o-headicon--question-mark
39
+ &:before
40
+ content: headicons.$question-mark
@@ -10,5 +10,6 @@ $jade-green: rgb(0, 190, 130)
10
10
  $granite-gray: rgb(70, 70, 70)
11
11
  $steel-gray: color.adjust($granite-gray, $lightness: 25%)
12
12
  $silver-gray: color.adjust($granite-gray, $lightness: 50%)
13
+ $moon-gray: color.adjust($granite-gray, $lightness: 65%)
13
14
 
14
15
  $transparent-gray: rgba(100, 100, 100, 0.5)
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Head
4
- class Knob < ApplicationComponent
4
+ class Knob < ::Head::ApplicationComponent
5
5
  erb_template <<~ERB
6
6
  <%= link_to(url, class: classes, data: { identifier: id, group: group }, **options.except(:url)) do %>
7
7
  <% if icon %>
8
- <i class="o-headicon <%= icon_class%><%= %>"></i>
8
+ <i class="o-headicon <%= icon_class%>"></i>
9
9
  <% end %>
10
10
 
11
11
  <%= content %>
@@ -28,6 +28,7 @@ module Head
28
28
  return :'bars-thin' if preset_mainmenu?
29
29
  return :bell if preset_notifications?
30
30
  return :gear if preset_settings?
31
+ return :'question-mark' if preset_help?
31
32
 
32
33
  :magnifier if preset_search?
33
34
  end
@@ -44,6 +45,7 @@ module Head
44
45
  result = %w[c-head-knob js-head-knob]
45
46
  result.push 'c-head-knob--identicon' if preset_identicon?
46
47
  result.push 'c-head-knob--avatar' if preset_avatar?
48
+ result.push 'c-head-knob--mainmenu' if preset_mainmenu?
47
49
  result
48
50
  end
49
51
 
@@ -67,6 +69,10 @@ module Head
67
69
  preset == :search
68
70
  end
69
71
 
72
+ def preset_help?
73
+ preset == :help
74
+ end
75
+
70
76
  def preset_identicon?
71
77
  preset == :identicon
72
78
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ class Sidebar < ::Head::ApplicationComponent
5
+ erb_template <<~ERB
6
+ <div data-group="<%= right ? :right : :left %>" class="c-head-sidebar <%= 'c-head-sidebar--right' if right %> ">
7
+ <% if categories.any? %>
8
+ <div class="c-head-sidebar__categories">
9
+ <% categories.each do |category| %>
10
+ <%= category %>
11
+ <% end %>
12
+ </div>
13
+
14
+ <div class="c-head-sidebar__secondary">
15
+ <% categories.select(&:submenu?).each do |category| %>
16
+ <div class="c-head-sidebar__links js-head-sidebar__links" data-identifier="<%= category.identifier %>">
17
+ <% category.links.each do |link| %>
18
+ <%= link %>
19
+ <% end %>
20
+ </div>
21
+ <% end %>
22
+ </div>
23
+ <% end %>
24
+ </div>
25
+ ERB
26
+
27
+ renders_many :categories, -> (**options) do
28
+ ::Head::Sidebars::Category.new(icon_class_prefix:, **options)
29
+ end
30
+
31
+ option :right, default: -> { false }
32
+ option :icon_class_prefix, default: -> {}
33
+ end
34
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ module Sidebars
5
+ class Category < ::Head::ApplicationComponent
6
+ erb_template <<~ERB
7
+ <%= link_to(url, data:,
8
+ class: 'c-head-sidebar__category js-head-sidebar__category', **link) do %>
9
+ <%= content_tag :i, nil, class: icon_classes %>
10
+ <%= content_tag :div, name, class: 'c-head-sidebar__category-name' %>
11
+ <% end %>
12
+ ERB
13
+
14
+ renders_many :links, -> (**options) do
15
+ ::Head::Sidebars::Link.new(icon_class_prefix:, **options)
16
+ end
17
+
18
+ option :url, default: -> { '#' }
19
+ option :icon_class_prefix, default: -> {}
20
+ option :name
21
+ option :icon
22
+ option :link, default: -> { {} }
23
+
24
+ def css_classes
25
+ "c-head-sidebar__category #{'js-head-sidebar__category' if submenu?}"
26
+ end
27
+
28
+ def icon_classes
29
+ "#{icon_class_prefix}#{icon.to_s.gsub('_', '-')} c-head-sidebar__category-icon"
30
+ end
31
+
32
+ def data
33
+ return unless submenu?
34
+
35
+ { identifier: }
36
+ end
37
+
38
+ def submenu?
39
+ url == '#'
40
+ end
41
+
42
+ def with_separator
43
+ with_link(name: nil, icon: nil)
44
+ end
45
+
46
+ def identifier
47
+ @identifier ||= SecureRandom.hex
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ module Sidebars
5
+ class Headline < ::Head::ApplicationComponent
6
+ erb_template <<~ERB
7
+ Headline
8
+ ERB
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ module Sidebars
5
+ class Link < ::Head::ApplicationComponent
6
+ erb_template <<~ERB
7
+ <%= link_to(url, class: link_classess, **link) do %>
8
+ <% if icon %>
9
+ <%= content_tag :i, nil, class: icon_classes %>
10
+ <% end %>
11
+ <%= content_tag :div, name, class: 'c-head-sidebar__link-name' %>
12
+ <% end %>
13
+ ERB
14
+
15
+ option :url, default: -> { '#' }
16
+ option :name
17
+ option :icon
18
+ option :link, default: -> { {} }
19
+ option :icon_class_prefix, default: -> {}
20
+ option :active, default: -> {}
21
+
22
+ def link_classess
23
+ "c-head-sidebar__link #{'c-head-sidebar__link-separator' unless icon} #{active?}".squish
24
+ end
25
+
26
+ def icon_classes
27
+ "#{icon_class_prefix}#{icon.to_s.gsub('_', '-')} c-head-sidebar__link-icon"
28
+ end
29
+
30
+ def active?
31
+ ::Head::Active.call(url: url_for(url),
32
+ condition: active,
33
+ current_path: request.original_fullpath)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Head
4
- class Theater < ApplicationComponent
4
+ class Theater < ::Head::ApplicationComponent
5
5
  erb_template <<~ERB
6
6
  <div class="c-head-theater">
7
7
  <div class="c-head-theater__roof">
@@ -50,6 +50,11 @@ module Head
50
50
  with_wing(id: :mainmenu, &)
51
51
  end
52
52
 
53
+ def with_help(&)
54
+ with_knob(preset: :help, right: true)
55
+ with_wing(id: :help, right: true, &)
56
+ end
57
+
53
58
  def with_notifications(&)
54
59
  with_knob(preset: :notifications, right: true)
55
60
  with_wing(id: :notifications, right: true, &)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Head
4
- class Wing < ApplicationComponent
4
+ class Wing < ::Head::ApplicationComponent
5
5
  erb_template <<~ERB
6
6
  <div class="<%= classes.join(' ') %>" data-identifier="<%= id %>" data-group="<%= group %>">
7
7
  <%= content %>
data/config/importmap.rb CHANGED
@@ -4,3 +4,4 @@ pin 'head', to: 'head/index.js'
4
4
 
5
5
  pin 'head/knob', to: 'head/knob.js'
6
6
  pin 'head/wing', to: 'head/wing.js'
7
+ pin 'head/sidebar', to: 'head/sidebar.js'
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Head
4
+ class Active
5
+ include Calls
6
+
7
+ option :url
8
+ option :current_path
9
+ option :condition
10
+
11
+ def call
12
+ :'is-active' if active?
13
+ end
14
+
15
+ private
16
+
17
+ def linked_path
18
+ ::URI.parse(url).path
19
+ end
20
+
21
+ def active?
22
+ return false if url == '#'
23
+
24
+ # Adapted from https://github.com/comfy/active_link_to (MIT license)
25
+ case condition
26
+
27
+ when :inclusive, nil # default
28
+ !current_path.match(%r{^#{Regexp.escape(linked_path).chomp('/')}(/.*|\?.*)?$}).blank?
29
+
30
+ when :exclusive
31
+ !current_path.match(%r{^#{Regexp.escape(linked_path)}/?(\?.*)?$}).blank?
32
+
33
+ when :exact
34
+ current_path == linked_path
35
+
36
+ when Regexp
37
+ !current_path.match(condition).blank?
38
+
39
+ when Array
40
+ controllers = [*condition[0]]
41
+ actions = [*condition[1]]
42
+ ((controllers.blank? || controllers.member?(params[:controller])) &&
43
+ (actions.blank? || actions.member?(params[:action]))) ||
44
+ controllers.any? do |controller, action|
45
+ params[:controller] == controller.to_s && params[:action] == action.to_s
46
+ end
47
+
48
+ when TrueClass
49
+ true
50
+
51
+ when FalseClass
52
+ false
53
+
54
+ when Hash
55
+ condition.all? do |key, value|
56
+ params[key].to_s == value.to_s
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
data/lib/head/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Head
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: head
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - halo
@@ -121,8 +121,10 @@ files:
121
121
  - app/assets/fonts/head/icomoon.json
122
122
  - app/assets/javascript/head/index.js
123
123
  - app/assets/javascript/head/knob.js
124
+ - app/assets/javascript/head/sidebar.js
124
125
  - app/assets/javascript/head/wing.js
125
126
  - app/assets/stylesheets/head/components/knob.sass
127
+ - app/assets/stylesheets/head/components/sidebar.sass
126
128
  - app/assets/stylesheets/head/components/theater.sass
127
129
  - app/assets/stylesheets/head/components/wing.sass
128
130
  - app/assets/stylesheets/head/generics/flip.sass
@@ -140,11 +142,16 @@ files:
140
142
  - app/assets/stylesheets/head/utilities/smokescreen.sass
141
143
  - app/components/head/application_component.rb
142
144
  - app/components/head/knob.rb
145
+ - app/components/head/sidebar.rb
146
+ - app/components/head/sidebars/category.rb
147
+ - app/components/head/sidebars/headline.rb
148
+ - app/components/head/sidebars/link.rb
143
149
  - app/components/head/theater.rb
144
150
  - app/components/head/wing.rb
145
151
  - config/importmap.rb
146
152
  - config/locales/head.en.yml
147
153
  - lib/head.rb
154
+ - lib/head/active.rb
148
155
  - lib/head/css.rb
149
156
  - lib/head/engine.rb
150
157
  - lib/head/identicon.rb