From 3a13563f0bb79057f132675ceceef4977c85f63b Mon Sep 17 00:00:00 2001 From: Paul Adam Davis Date: Mon, 29 Sep 2014 12:17:27 +0100 Subject: [PATCH] Implement popovers --- .../assets/sass/components/dropdowns.scss | 24 +- .../assets/sass/components/popovers.scss | 265 ++++++++++++++++++ ghost/admin/assets/sass/helpers/mixins.scss | 16 +- .../admin/assets/sass/helpers/variables.scss | 2 + ghost/admin/assets/sass/screen.scss | 1 + ghost/admin/components/gh-dropdown.js | 2 +- ghost/admin/components/gh-popover-button.js | 15 + ghost/admin/components/gh-popover.js | 7 + ghost/admin/docs/popovers.html | 75 +++++ ghost/admin/initializers/dropdown.js | 23 +- ghost/admin/templates/debug.hbs | 3 +- ghost/admin/utils/dropdown-service.js | 17 ++ 12 files changed, 415 insertions(+), 35 deletions(-) create mode 100644 ghost/admin/assets/sass/components/popovers.scss create mode 100644 ghost/admin/components/gh-popover-button.js create mode 100644 ghost/admin/components/gh-popover.js create mode 100644 ghost/admin/docs/popovers.html create mode 100644 ghost/admin/utils/dropdown-service.js diff --git a/ghost/admin/assets/sass/components/dropdowns.scss b/ghost/admin/assets/sass/components/dropdowns.scss index 85b9275cb0..20bef83f8e 100644 --- a/ghost/admin/assets/sass/components/dropdowns.scss +++ b/ghost/admin/assets/sass/components/dropdowns.scss @@ -258,52 +258,52 @@ %dropdown-triangle-top { &:before { - @include triangle(($dropdown_triangle * 2), #fff, up); + @include triangle($dropdown_triangle, #fff, up); top: -$dropdown_triangle; } &:after { - @include triangle(($dropdown_triangle * 2) + 2, darken($lightgrey, 15%), up); - top: -($dropdown_triangle + 1); + @include triangle($dropdown_triangle + 2, darken($lightgrey, 15%), up); + top: -($dropdown_triangle + 2); } } %dropdown-triangle-bottom { &:before { - @include triangle(($dropdown_triangle * 2), #fff, down); + @include triangle($dropdown_triangle, #fff, down); bottom: -$dropdown_triangle; } &:after { - @include triangle(($dropdown_triangle * 2) + 2, darken($lightgrey, 15%), down); - bottom: -($dropdown_triangle + 1); + @include triangle($dropdown_triangle + 2, darken($lightgrey, 15%), down); + bottom: -($dropdown_triangle + 2); } } %dropdown-triangle-center { &:before { left: 50%; - margin-left: (-$dropdown_triangle); + margin-left: -($dropdown_triangle / 2); } &:after { left: 50%; - margin-left: -($dropdown_triangle + 1); + margin-left: -($dropdown_triangle / 2 + 2); } } // TODO: Make the values here use the $dropdown_triangle var %dropdown-triangle-left { &:before { - left: ($dropdown_triangle + 2); + left: ($dropdown_triangle / 2 + 2); } &:after { - left: ($dropdown_triangle + 1); + left: ($dropdown_triangle / 2); } } %dropdown-triangle-right { &:before { left: auto; - right: ($dropdown_triangle + 2); + right: ($dropdown_triangle / 2 + 2); } &:after { left: auto; - right: ($dropdown_triangle + 1); + right: ($dropdown_triangle / 2); } } diff --git a/ghost/admin/assets/sass/components/popovers.scss b/ghost/admin/assets/sass/components/popovers.scss new file mode 100644 index 0000000000..41642a854a --- /dev/null +++ b/ghost/admin/assets/sass/components/popovers.scss @@ -0,0 +1,265 @@ +// +// Popovers +// -------------------------------------------------- + +.popover-item { + position: relative; + display: inline-block; + padding: 11px 26px 13px 16px; + background: $darkgrey; + min-width: 300px; + max-width: 400px; + border-radius: 6px; + font-size: 1.2rem; + color: $midgrey; +} +.popover-title { + font-size: 1.4rem; + font-weight: 300; + color: #fff; +} +.popover-desc { + margin-top: -4px; +} +.popover-body { + margin-top: 11px; + line-height: 1.7; + b { + color: #fff; + } + > *:last-child { + margin: 0; + } +} + +// +// Popover triangles +// -------------------------------------------------- + +// Placeholders +%popover-triangle { + &:before { + content: ''; + position: absolute; + display: block; + } // :before +} + +// The triangle itself +%popover-triangle-vertical-top { + &:before { + @include triangle($popover_triangle, $darkgrey, up, shallow); + top: -(floor($popover_triangle * $popover_triangle_shallow_multiplier)); + } +} +%popover-triangle-vertical-bottom { + &:before { + @include triangle($popover_triangle, $darkgrey, down, shallow); + bottom: -(floor($popover_triangle * $popover_triangle_shallow_multiplier)); + } +} +%popover-triangle-horizontal-left { + &:before { + @include triangle($popover_triangle, $darkgrey, left, shallow); + left: -(floor($popover_triangle * $popover_triangle_shallow_multiplier)); + } +} +%popover-triangle-horizontal-right { + &:before { + @include triangle($popover_triangle, $darkgrey, right, shallow); + right: -(floor($popover_triangle * $popover_triangle_shallow_multiplier)); + } +} + +// Triangle positions +%popover-triangle-vertical-center { + &:before { + left: 50%; + margin-left: -($popover_triangle / 2); + } +} +%popover-triangle-vertical-left { + &:before { + left: $popover_triangle; + } +} +%popover-triangle-vertical-right { + &:before { + left: auto; + right: $popover_triangle; + } +} +%popover-triangle-horizontal-center { + &:before { + top: 50%; + margin-top: -$popover_triangle; + } +} +%popover-triangle-horizontal-top { + &:before { + top: $popover_triangle; + } +} +%popover-triangle-horizontal-bottom { + &:before { + top: auto; + bottom: $popover_triangle; + } +} + +// Usable classes +.popover-triangle-top { + transform-origin: top center; + @extend %popover-triangle; + @extend %popover-triangle-vertical-top; + @extend %popover-triangle-vertical-center; +} +.popover-triangle-top-left { + transform-origin: top left; + @extend %popover-triangle; + @extend %popover-triangle-vertical-top; + @extend %popover-triangle-vertical-left; +} +.popover-triangle-top-right { + transform-origin: top right; + @extend %popover-triangle; + @extend %popover-triangle-vertical-top; + @extend %popover-triangle-vertical-right; +} +.popover-triangle-bottom { + transform-origin: bottom center; + @extend %popover-triangle; + @extend %popover-triangle-vertical-bottom; + @extend %popover-triangle-vertical-center; +} +.popover-triangle-bottom-left { + transform-origin: bottom left; + @extend %popover-triangle; + @extend %popover-triangle-vertical-bottom; + @extend %popover-triangle-vertical-left; +} +.popover-triangle-bottom-right { + transform-origin: bottom right; + @extend %popover-triangle; + @extend %popover-triangle-vertical-bottom; + @extend %popover-triangle-vertical-right; +} +.popover-triangle-left { + transform-origin: left center; + @extend %popover-triangle; + @extend %popover-triangle-horizontal-left; + @extend %popover-triangle-horizontal-center; +} +.popover-triangle-left-top { + transform-origin: left top; + @extend %popover-triangle; + @extend %popover-triangle-horizontal-left; + @extend %popover-triangle-horizontal-top; +} +.popover-triangle-left-bottom { + transform-origin: left bottom; + @extend %popover-triangle; + @extend %popover-triangle-horizontal-left; + @extend %popover-triangle-horizontal-bottom; +} +.popover-triangle-right { + transform-origin: right center; + @extend %popover-triangle; + @extend %popover-triangle-horizontal-right; + @extend %popover-triangle-horizontal-center; +} +.popover-triangle-right-top { + transform-origin: right top; + @extend %popover-triangle; + @extend %popover-triangle-horizontal-right; + @extend %popover-triangle-horizontal-top; +} +.popover-triangle-right-bottom { + transform-origin: right bottom; + @extend %popover-triangle; + @extend %popover-triangle-horizontal-right; + @extend %popover-triangle-horizontal-bottom; +} + +// Show/hide popover +// Position relative to the position of the triangle +// So... `popover-triangle-left-top` opens on the right of the button +// because the triangle is on the top left, poitning to the top right of the button +// +// |------| |-----------------| +// |Button| < Popover content | +// |------| | Lorem ipsum dol | +// |-----------------| +.popover { + position: relative; + display: inline-block; + + .popover-item { + position: absolute; + z-index: 20; + + &.open { + display: block; + } + &.closed { + display: none; + } + } + + .popover-item.popover-triangle-bottom { + bottom: calc(100% + 16px); + left: 50%; + transform: translateX(-50%); + } + .popover-item.popover-triangle-bottom-left { + bottom: calc(100% + 16px); + left: 0; + } + .popover-item.popover-triangle-bottom-right { + bottom: calc(100% + 16px); + right: 0; + } + .popover-item.popover-triangle-top { + top: calc(100% + 16px); + left: 50%; + transform: translateX(-50%); + } + .popover-item.popover-triangle-top-left { + top: calc(100% + 16px); + left: 0; + } + .popover-item.popover-triangle-top-right { + top: calc(100% + 16px); + right: 0; + } + .popover-item.popover-triangle-left { + left: calc(100% + 16px); + top: 50%; + transform: translateY(-50%); + } + .popover-item.popover-triangle-left-top { + left: calc(100% + 16px); + top: 50%; + transform: translateY(-($popover_triangle * 2)); + } + .popover-item.popover-triangle-left-bottom { + left: calc(100% + 16px); + top: 50%; + transform: translateY(calc(-100% + #{($popover_triangle * 2)})); + } + .popover-item.popover-triangle-right { + right: calc(100% + 16px); + top: 50%; + transform: translateY(-50%); + } + .popover-item.popover-triangle-right-top { + right: calc(100% + 16px); + top: 50%; + transform: translateY(-($popover_triangle * 2)); + } + .popover-item.popover-triangle-right-bottom { + right: calc(100% + 16px); + top: 50%; + transform: translateY(calc(-100% + #{($popover_triangle * 2)})); + } +}//.popover \ No newline at end of file diff --git a/ghost/admin/assets/sass/helpers/mixins.scss b/ghost/admin/assets/sass/helpers/mixins.scss index 98830fe8ea..4b58757359 100644 --- a/ghost/admin/assets/sass/helpers/mixins.scss +++ b/ghost/admin/assets/sass/helpers/mixins.scss @@ -103,31 +103,35 @@ //==== Simple SCSS mixin to create CSS triangles //==== Example: @include css-triangle (10px, #fff, "up"); -@mixin triangle ($size: 20px, $color: #000, $direction: "down") { - $size: $size / 2; +@mixin triangle ($size: 20px, $color: #000, $direction: "down", $type: "normal") { + $verticalSize: $size; width: 0; height: 0; + @if $type == "shallow" { + $verticalSize: floor($size * $popover_triangle_shallow_multiplier); + } + @if $direction == "down" { border-left: $size solid #{setTriangleColor($direction, "left", $color)}; border-right: $size solid #{setTriangleColor($direction, "right", $color)}; - border-top: $size solid #{setTriangleColor($direction, "top", $color)}; + border-top: $verticalSize solid #{setTriangleColor($direction, "top", $color)}; } @if $direction == "up" { border-left: $size solid #{setTriangleColor($direction, "left", $color)}; border-right: $size solid #{setTriangleColor($direction, "right", $color)}; - border-bottom: $size solid #{setTriangleColor($direction, "bottom", $color)}; + border-bottom: $verticalSize solid #{setTriangleColor($direction, "bottom", $color)}; } @if $direction == "left" { - border-right: $size solid #{setTriangleColor($direction, "right", $color)}; + border-right: $verticalSize solid #{setTriangleColor($direction, "right", $color)}; border-top: $size solid #{setTriangleColor($direction, "top", $color)}; border-bottom: $size solid #{setTriangleColor($direction, "bottom", $color)}; } @if $direction == "right" { - border-left: $size solid #{setTriangleColor($direction, "left", $color)}; + border-left: $verticalSize solid #{setTriangleColor($direction, "left", $color)}; border-bottom: $size solid #{setTriangleColor($direction, "bottom", $color)}; border-top: $size solid #{setTriangleColor($direction, "top", $color)}; } diff --git a/ghost/admin/assets/sass/helpers/variables.scss b/ghost/admin/assets/sass/helpers/variables.scss index 4123e0a3d6..42dc6232c1 100644 --- a/ghost/admin/assets/sass/helpers/variables.scss +++ b/ghost/admin/assets/sass/helpers/variables.scss @@ -45,6 +45,8 @@ $at2x: 2 device-pixel-ratio; $dropdown_triangle: 8px; +$popover_triangle: 14px; +$popover_triangle_shallow_multiplier: 0.8; diff --git a/ghost/admin/assets/sass/screen.scss b/ghost/admin/assets/sass/screen.scss index 1bd769f714..568eafab09 100644 --- a/ghost/admin/assets/sass/screen.scss +++ b/ghost/admin/assets/sass/screen.scss @@ -43,6 +43,7 @@ @import "components/dropdowns"; @import "components/pagination"; @import "components/badges"; +@import "components/popovers"; // diff --git a/ghost/admin/components/gh-dropdown.js b/ghost/admin/components/gh-dropdown.js index 3d4c55862e..e825e2960e 100644 --- a/ghost/admin/components/gh-dropdown.js +++ b/ghost/admin/components/gh-dropdown.js @@ -42,7 +42,7 @@ var GhostDropdown = Ember.Component.extend(DropdownMixin, { name = this.get('name'), button = this.get('button'), targetDropdownName = options.target; - + if (name === targetDropdownName && (!isOpen || isClosing)) { if (!button) { button = options.button; diff --git a/ghost/admin/components/gh-popover-button.js b/ghost/admin/components/gh-popover-button.js new file mode 100644 index 0000000000..598cd473bc --- /dev/null +++ b/ghost/admin/components/gh-popover-button.js @@ -0,0 +1,15 @@ +import DropdownButton from 'ghost/components/gh-dropdown-button'; + +var PopoverButton = DropdownButton.extend({ + click: Ember.K, // We don't want clicks on popovers, but dropdowns have them. So `K`ill them here. + mouseEnter: function (event) { + this._super(event); + this.get('dropdown').toggleDropdown(this.get('popoverName'), this); + }, + mouseLeave: function (event) { + this._super(event); + this.get('dropdown').toggleDropdown(this.get('popoverName'), this); + } +}); + +export default PopoverButton; \ No newline at end of file diff --git a/ghost/admin/components/gh-popover.js b/ghost/admin/components/gh-popover.js new file mode 100644 index 0000000000..bc437b16dc --- /dev/null +++ b/ghost/admin/components/gh-popover.js @@ -0,0 +1,7 @@ +import GhostDropdown from 'ghost/components/gh-dropdown'; + +var GhostPopover = GhostDropdown.extend({ + classNames: 'ghost-popover' +}); + +export default GhostPopover; \ No newline at end of file diff --git a/ghost/admin/docs/popovers.html b/ghost/admin/docs/popovers.html new file mode 100644 index 0000000000..83822c16f5 --- /dev/null +++ b/ghost/admin/docs/popovers.html @@ -0,0 +1,75 @@ +--- +layout: default +title: Popovers · Ghost UI +--- + + + +
+ +

Popovers

+ +
+ +
+
URL Structure Formatting
+
You can use dynamic variables in this field.
+
+

+ %t - The title of your post (or page)
+ %c - The tag which your post is categorised in
+ %y - The year your post was published
+ %m - The month your post was published
+ %d - The day your post was published +

+
+
+ +
+ + {% assign popover_classes = "popover-triangle-top|popover-triangle-top-left|popover-triangle-top-right|popover-triangle-bottom|popover-triangle-bottom-left|popover-triangle-bottom-right|popover-triangle-right|popover-triangle-right-top|popover-triangle-right-bottom|popover-triangle-left|popover-triangle-left-top|popover-triangle-left-bottom" | split: "|" %} + {% for item in popover_classes %} +
+ {{item}} +
+
URL Structure Formatting
+
You can use dynamic variables in this field.
+
+

+ %t - The title of your post (or page)
+ %c - The tag which your post is categorised in
+ %y - The year your post was published
+ %m - The month your post was published
+ %d - The day your post was published +

+
+
+
+

+ {% endfor %} +
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/ghost/admin/initializers/dropdown.js b/ghost/admin/initializers/dropdown.js index 034691b996..7e850d10a9 100644 --- a/ghost/admin/initializers/dropdown.js +++ b/ghost/admin/initializers/dropdown.js @@ -1,17 +1,4 @@ -import BodyEventListener from 'ghost/mixins/body-event-listener'; - -var DropdownService = Ember.Object.extend(Ember.Evented, BodyEventListener, { - bodyClick: function (event) { - /*jshint unused:false */ - this.closeDropdowns(); - }, - closeDropdowns: function () { - this.trigger('close'); - }, - toggleDropdown: function (dropdownName, dropdownButton) { - this.trigger('toggle', {target: dropdownName, button: dropdownButton}); - } -}); +import DropdownService from 'ghost/utils/dropdown-service'; var dropdownInitializer = { name: 'dropdown', @@ -19,12 +6,18 @@ var dropdownInitializer = { initialize: function (container, application) { application.register('dropdown:service', DropdownService); + // Inject dropdowns application.inject('component:gh-dropdown', 'dropdown', 'dropdown:service'); application.inject('component:gh-dropdown-button', 'dropdown', 'dropdown:service'); application.inject('controller:modals.delete-post', 'dropdown', 'dropdown:service'); application.inject('controller:modals.transfer-owner', 'dropdown', 'dropdown:service'); application.inject('route:application', 'dropdown', 'dropdown:service'); + + // Inject popovers + application.inject('component:gh-popover', 'dropdown', 'dropdown:service'); + application.inject('component:gh-popover-button', 'dropdown', 'dropdown:service'); + application.inject('route:application', 'dropdown', 'dropdown:service'); } }; -export default dropdownInitializer; +export default dropdownInitializer; \ No newline at end of file diff --git a/ghost/admin/templates/debug.hbs b/ghost/admin/templates/debug.hbs index 6578315369..25936f04be 100644 --- a/ghost/admin/templates/debug.hbs +++ b/ghost/admin/templates/debug.hbs @@ -6,7 +6,7 @@
- +

Ugly Debug Tools

@@ -15,6 +15,7 @@
+
diff --git a/ghost/admin/utils/dropdown-service.js b/ghost/admin/utils/dropdown-service.js new file mode 100644 index 0000000000..95a6cb6844 --- /dev/null +++ b/ghost/admin/utils/dropdown-service.js @@ -0,0 +1,17 @@ +// This is used by the dropdown initializer (and subsequently popovers) to manage closing & toggeling +import BodyEventListener from 'ghost/mixins/body-event-listener'; + +var DropdownService = Ember.Object.extend(Ember.Evented, BodyEventListener, { + bodyClick: function (event) { + /*jshint unused:false */ + this.closeDropdowns(); + }, + closeDropdowns: function () { + this.trigger('close'); + }, + toggleDropdown: function (dropdownName, dropdownButton) { + this.trigger('toggle', {target: dropdownName, button: dropdownButton}); + } +}); + +export default DropdownService; \ No newline at end of file