1 <?php
2
3 /*
4 * This file is part of the Brickrouge package.
5 *
6 * (c) Olivier Laviale <olivier.laviale@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Brickrouge;
13
14 /**
15 * Creates a popover element.
16 *
17 * Brickrouge provides the Brickrouge.Popover Javascript class that can be used to give behaviour
18 * to the element, but because the element is not a widget this is not automatic and left up to
19 * you.
20 *
21 * Use the Brickrouge\PopoverWidget to create elements with automatically attached behaviour.
22 */
23 class Popover extends Element
24 {
25 /**
26 * Popover actions.
27 *
28 * @var string|array
29 */
30 const ACTIONS = '#actions';
31
32 /**
33 * Anchor ID or CSS selector.
34 *
35 * @var string
36 */
37 const ANCHOR = '#anchor';
38
39 /**
40 * Whether the popover element should fit the content.
41 *
42 * By default the popover element have a width of 280px. Setting this attribute to true adds
43 * the 'fit-content' class to the element which remove the width constraint.
44 *
45 * @var bool
46 */
47 const FIT_CONTENT = '#fit-content';
48
49 /**
50 * Placement of the popover relative to its anchor, one of `before`, `after`, `above`,
51 * `below`, `vertical`, `horizontal` or `auto`.
52 *
53 * @var string
54 */
55 const PLACEMENT = '#placement';
56
57 /**
58 * Optional title of the popover.
59 *
60 * @var string
61 */
62 const TITLE = '#title';
63
64 /**
65 * Constructor.
66 *
67 * The `class` attribute is defined in the initial tags with the value "popover". The "popover"
68 * class is used to style the element but can also be used to give the element a behaviour. If
69 * you override the `class` attribute you should consider adding the "popover" class name.
70 *
71 * The element is created as a DIV element.
72 *
73 * @param array $tags
74 */
75 public function __construct(array $tags=array())
76 {
77 parent::__construct
78 (
79 'div', $tags + array
80 (
81 'class' => 'popover'
82 )
83 );
84 }
85
86 /**
87 * Adds the 'fit-content' class name if the {@link FIT_CONTENT} attribute is truthy.
88 *
89 * @see Brickrouge.Element::alter_class_names()
90 */
91 protected function alter_class_names(array $class_names)
92 {
93 $class_names = parent::alter_class_names($class_names);
94
95 if ($this[self::FIT_CONTENT])
96 {
97 $class_names['fit-content'] = true;
98 }
99
100 return $class_names;
101 }
102
103 /**
104 * Adds the anchor specified using the {@link ANCHOR} special attribute to the dataset before
105 * it is rendered.
106 *
107 * @see Brickrouge.Element::alter_dataset()
108 */
109 protected function alter_dataset(array $dataset)
110 {
111 return parent::alter_dataset
112 (
113 $dataset + array
114 (
115 'anchor' => $this[self::ANCHOR],
116 'placement' => $this[self::PLACEMENT]
117 )
118 );
119 }
120
121 /**
122 * The inner HTML is wrapped in a number of DIV elements, and the title is used a the popover
123 * title.
124 *
125 * @see Brickrouge.Element::render_inner_html()
126 */
127 protected function render_inner_html()
128 {
129 $content = parent::render_inner_html();
130
131 $title = $this[self::TITLE];
132
133 if ($title)
134 {
135 $title = '<h3 class="popover-title">' . escape($title) . '</h3>';
136 }
137
138 $actions = $this[self::ACTIONS];
139
140 if ($actions)
141 {
142 $actions = $this->render_actions($actions);
143 }
144
145 return <<<EOT
146 <div class="arrow"></div>
147 <div class="popover-inner">$title<div class="popover-content">$content</div>$actions</div>
148 EOT;
149 }
150
151 /**
152 * Renders actions.
153 *
154 * Actions are rendering using a {@link Actions} element.
155 *
156 * Actions buttons should provide a `data-action` attribute with the value of the action to use
157 * when the `action` event is fired by Javascript.
158 *
159 * @param mixed $actions
160 *
161 * @return string
162 */
163 protected function render_actions($actions)
164 {
165 return new Actions($actions, array('class' => 'popover-actions'));
166 }
167 }
168
169 /**
170 * A popover element with automatically attached behaviour.
171 */
172 class PopoverWidget extends Popover
173 {
174 /**
175 * Whether the widget should be made visible once elements are ready.
176 *
177 * @var bool
178 */
179 const VISIBLE = '#visible';
180
181 /**
182 * Overrides the {@link Popover} initial attribute `class` with the value
183 * "widget-popover popover". The "widget-popover" class is used to automatically attach
184 * popover behaviour to the element, while the "popover" class is used to style the element.
185 *
186 * If you override the `class` attribute, remember to define this two class names, unless
187 * you want to use a different behaviour or style.
188 *
189 * @param array $tags
190 */
191 public function __construct(array $tags)
192 {
193 parent::__construct
194 (
195 $tags + array
196 (
197 self::WIDGET_CONSTRUCTOR => 'Popover',
198
199 'class' => 'widget-popover popover'
200 )
201 );
202 }
203
204 /**
205 * Adds the `visible` property to the dataset.
206 *
207 * @see Brickrouge.Popover::alter_dataset()
208 */
209 protected function alter_dataset(array $dataset)
210 {
211 return parent::alter_dataset
212 (
213 $dataset + array
214 (
215 'visible' => $this[self::VISIBLE]
216 )
217 );
218 }
219 }