DBColour   C
last analyzed

Complexity

Total Complexity 53

Size/Duplication

Total Lines 402
Duplicated Lines 0 %

Importance

Changes 14
Bugs 1 Features 0
Metric Value
wmc 53
eloc 148
c 14
b 1
f 0
dl 0
loc 402
rs 6.96

28 Methods

Rating   Name   Duplication   Size   Complexity  
A get_colours_for_dropdown() 0 32 5
A my_colours() 0 3 1
A is_dark_colour() 0 3 2
A get_swatches_field() 0 7 1
A getCssClass() 0 10 2
A getIsLightColour() 0 3 1
A getIsDarkColour() 0 3 2
A scaffoldFormField() 0 8 2
A __construct() 0 3 1
A is_light_colour() 0 4 1
A get_dropdown_field() 0 14 2
A getRelatedColours() 0 5 1
A getCssVarLine() 0 5 2
A check_colour() 0 18 5
A getRelatedColourByName() 0 6 1
A getCssClassAlternative() 0 9 3
A get_font_colour() 0 8 3
A getNice() 0 20 1
A getReadableColour() 0 4 1
A kebabCase() 0 3 1
A getBackgroundColour() 0 7 2
A Inverted() 0 18 1
A get_colour_as_db_field() 0 8 3
A getColours() 0 3 1
A classCleanup() 0 6 1
A getFontColour() 0 7 2
A getMyColours() 0 5 2
A getCssVariableDefinition() 0 20 3

How to fix   Complexity   

Complex Class

Complex classes like DBColour often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DBColour, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Sunnysideup\SelectedColourPicker\Model\Fields;
4
5
use BimTheBam\NativeColorInput\Form\Field\ColorField;
6
use Fromholdio\ColorPalette\Fields\ColorPaletteField;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Forms\FormField;
9
use SilverStripe\Forms\LiteralField;
10
use SilverStripe\ORM\FieldType\DBField;
11
use Sunnysideup\SelectedColourPicker\Forms\SelectedColourPickerFormFieldDropdown;
12
use Sunnysideup\SelectedColourPicker\ViewableData\SelectedColourPickerFormFieldSwatches;
13
use TractorCow\Colorpicker\Color;
14
15
class DBColour extends Color
16
{
17
    protected const DEFAULT_COLOURS = [
18
        '#FF0000' => 'Red',
19
        '#0000FF' => 'Blue',
20
        '#00FF00' => 'Green',
21
    ];
22
23
    /**
24
     * please set.
25
     *
26
     * @var string`
27
     */
28
    protected const CSS_CLASS_PREFIX = 'db-colour';
29
30
    /**
31
     * please set.
32
     *
33
     * @var bool
34
     */
35
    protected const IS_LIMITED_TO_OPTIONS = true;
36
37
    /**
38
     * please set.
39
     *
40
     * @var bool
41
     */
42
    protected const IS_BG_COLOUR = true;
43
44
    protected static $object_cache = [];
45
    private static $colour_picker_field_class_name = SelectedColourPickerFormFieldDropdown::class;
46
47
    /**
48
     * please set
49
     * must be defined as #AABB99 (hex codes).
50
     * Needs to be set like this:
51
     * ```php
52
     *     [
53
     *         '#fff000' => 'My Colour 1',
54
     *         '#fff000' => 'My Colour 2',
55
     *     ].
56
     *
57
     * ```
58
     *
59
     * @var array
60
     */
61
    private static $colours = [];
62
63
    /**
64
     * You can link colours to other colours.
65
     * e.g.
66
     * ```php
67
     *     '#ffffff' => [
68
     *          'link' => '#000000',
69
     *          'foreground' => '#000000',
70
     *          'background' => '#000000',
71
     *     ],
72
     *     '#aabbcc' => [
73
     *          'link' => '#123123',
74
     *          'foreground' => '#123312',
75
     *          'somethingelse' => '#000000',
76
     *     ],
77
     * ```.
78
     *
79
     * @var array
80
     */
81
    private static $linked_colours = [];
82
83
    private static $casting = [
84
        // related colours
85
        'FontColour' => 'Varchar',
86
        'BackgroundColour' => 'Varchar',
87
        'ReadableColour' => 'Varchar',
88
        'RelatedColourByName' => 'Varchar',
89
        'Inverted' => 'Varchar',
90
        // css
91
        'CssVariableDefinition' => 'HTMLText',
92
        'CssClass' => 'Varchar',
93
        'CssClassAlternative' => 'Boolean',
94
        // booleans
95
        'IsDarkColour' => 'Boolean',
96
        'IsLightColour' => 'Boolean',
97
98
        'Nice' => 'HTMLText',
99
    ];
100
101
    public function __construct($name = null, $options = [])
102
    {
103
        parent::__construct($name, $options);
104
    }
105
106
    public static function my_colours(): array
107
    {
108
        return static::get_colour_as_db_field('')->getColours();
109
    }
110
111
    public static function get_swatches_field(string $name, string $value): LiteralField
112
    {
113
        return SelectedColourPickerFormFieldSwatches::get_swatches_field(
114
            (string) $name,
115
            (string) $value,
116
            static::my_colours(),
117
            static::IS_BG_COLOUR
118
        );
119
    }
120
121
    /**
122
     * @param string $title
123
     *
124
     * @return FormField
125
     */
126
    public static function get_dropdown_field(string $name, ?string $title = '', ?bool $isBackgroundColour = null)
127
    {
128
        if (null === $isBackgroundColour) {
129
            $isBackgroundColour = static::IS_BG_COLOUR;
130
        }
131
        $className = Config::inst()->get(static::class, 'colour_picker_field_class_name');
132
133
        return $className::create(
134
            $name,
135
            $title
136
        )
137
            ->setSource(static::my_colours())
138
            ->setLimitedToOptions(static::IS_LIMITED_TO_OPTIONS)
139
            ->setIsBgColour($isBackgroundColour)
140
        ;
141
    }
142
143
    public static function get_colours_for_dropdown(?bool $isBackgroundColour = null): ?array
144
    {
145
        if (null === $isBackgroundColour) {
146
            $isBackgroundColour = static::IS_BG_COLOUR;
147
        }
148
        $colours = static::my_colours();
149
        if (! empty($colours)) {
150
            $array = [];
151
152
            foreach ($colours as $code => $label) {
153
                $textcolour = static::get_font_colour((string) $code);
154
                if ($isBackgroundColour) {
155
                    $array[$code] = [
156
                        'label' => $label,
157
                        'background_css' => $code,
158
                        'colour_css' => $textcolour,
159
                        'sample_text' => 'Aa',
160
                    ];
161
                } else {
162
                    $array[$code] = [
163
                        'label' => $label,
164
                        'background_css' => $textcolour,
165
                        'colour_css' => $code,
166
                        'sample_text' => 'Aa',
167
                    ];
168
                }
169
            }
170
171
            return $array;
172
        }
173
174
        return null;
175
    }
176
177
    /**
178
     * Detects if the given colour is light.
179
     *
180
     * @param string $colour HEX colour code
181
     */
182
    public static function get_font_colour(?string $colour = null, ?string $name = '')
183
    {
184
        if (! $colour) {
185
            $colour = '#ffffff';
186
        }
187
        $colour = static::is_light_colour((string) $colour) ? '#000000' : '#ffffff';
188
189
        return static::get_colour_as_db_field($colour, $name);
190
    }
191
192
    /**
193
     * @param string $colour HEX colour code
194
     */
195
    public static function is_dark_colour(?string $colour = ''): bool
196
    {
197
        return static::is_light_colour((string) $colour) ? false : true;
198
    }
199
200
    /**
201
     * Detects if the given colour is light.
202
     *
203
     * @param string $colour HEX colour code
204
     */
205
    public static function is_light_colour(?string $colour = ''): bool
206
    {
207
        return static::get_colour_as_db_field($colour)
0 ignored issues
show
Bug introduced by
It seems like $colour can also be of type null; however, parameter $colour of Sunnysideup\SelectedColo...et_colour_as_db_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
        return static::get_colour_as_db_field(/** @scrutinizer ignore-type */ $colour)
Loading history...
208
            ->Luminance() > 0.5;
209
    }
210
211
    public static function check_colour(?string $colour, ?bool $isBackgroundColour = false): string
212
    {
213
        $colour = strtolower($colour);
0 ignored issues
show
Bug introduced by
It seems like $colour can also be of type null; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

213
        $colour = strtolower(/** @scrutinizer ignore-type */ $colour);
Loading history...
214
        if ('transparent' === $colour) {
215
            return 'transparent';
216
        }
217
        if (! strpos($colour, '#')) {
218
            $colour = '#' . $colour;
219
        }
220
        if (! $colour) {
221
            if ($isBackgroundColour) {
222
                $colour = '#ffffff';
223
            } else {
224
                $colour = '#000000';
225
            }
226
        }
227
228
        return $colour;
229
    }
230
231
    public function scaffoldFormField($title = null, $params = null)
232
    {
233
        $array = static::get_colours_for_dropdown();
234
        if (empty($array)) {
235
            return ColorField::create($this->name, $title);
236
        }
237
238
        return ColorPaletteField::create($this->name, $title, static::get_colours_for_dropdown());
239
    }
240
241
    public function getReadableColour(): static
242
    {
243
        // Remove '#' if it's present
244
        return static::get_font_colour((string) $this->value, $this->name);
245
    }
246
247
    public function Inverted(): static
248
    {
249
        // Ensure the colour is 6 characters long
250
        $colour = str_pad(ltrim($this->value, '#'), 6, '0', STR_PAD_RIGHT);
251
252
        // Convert the colour to decimal
253
        $colour = hexdec($colour);
254
255
        // Invert the colour
256
        $colour = 0xFFFFFF - $colour;
257
258
        // Convert the colour back to hex
259
        $colour = dechex($colour);
0 ignored issues
show
Bug introduced by
$colour of type double is incompatible with the type integer expected by parameter $num of dechex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

259
        $colour = dechex(/** @scrutinizer ignore-type */ $colour);
Loading history...
260
261
        // Ensure the colour is 6 characters long
262
        $colour = str_pad($colour, 6, '0', STR_PAD_LEFT);
263
264
        return static::get_colour_as_db_field($colour, $this->name);
265
    }
266
267
    public function getRelatedColourByName(string $relatedName): static
268
    {
269
        $relatedColours = $this->getRelatedColours($this->value);
0 ignored issues
show
Unused Code introduced by
The call to Sunnysideup\SelectedColo...ur::getRelatedColours() has too many arguments starting with $this->value. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

269
        /** @scrutinizer ignore-call */ 
270
        $relatedColours = $this->getRelatedColours($this->value);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
270
        $colour = $relatedColours[$relatedName] ?? 'error';
271
272
        return static::get_colour_as_db_field($colour, $this->name);
273
    }
274
275
    public function getCssVariableDefinition(?string $rootElement = ':root'): string
276
    {
277
        $style = PHP_EOL . '<style>';
278
        $style .= PHP_EOL . $rootElement;
279
        $style .= PHP_EOL . '{';
280
        $style .= $this->getCssVarLine();
281
        if (static::IS_BG_COLOUR) {
282
            $readableColourObj = $this->getReadableColour();
283
            $style .= $readableColourObj->getCssVarLine('-font');
284
        }
285
        foreach ($this->getRelatedColours() as $name => $relatedColour) {
286
            $relatedColourObj = self::get_colour_as_db_field($relatedColour, $this->name);
287
            $style .= $relatedColourObj->getCssVarLine($name);
288
            $relatedColourObjReadable = $relatedColourObj->getReadableColour();
289
            $style .= $relatedColourObjReadable->getCssVarLine($name . '-font');
290
        }
291
        $style .= PHP_EOL . '}';
292
        $style .= PHP_EOL . '</style>';
293
294
        return $style;
295
    }
296
297
    public function getCssVarLine(?string $name = '', ?string $prefix = '--colour-'): string
298
    {
299
        $variableName = '    ' . $prefix;
300
        $variableName .= $name ?: $this->kebabCase($this->getName());
301
        return PHP_EOL . $variableName . ': ' . $this->getValue() . ';';
302
    }
303
304
    public function getCssClass(?bool $isTransparent = false): string
305
    {
306
        $colours = $this->getColours();
307
        if ($isTransparent) {
308
            $name = 'transparent';
309
        } else {
310
            $name = $colours[$this->value] ?? 'colour-error';
311
        }
312
313
        return $this->classCleanup($name);
314
    }
315
316
    public function getCssClassAlternative(?bool $isTransparent = false): string
317
    {
318
        if ($isTransparent) {
319
            $name = 'ffffff00';
320
        } else {
321
            $name = $this->value ?: 'no-colour';
322
        }
323
324
        return $this->classCleanup($name);
325
    }
326
327
    public function getIsLightColour(): bool
328
    {
329
        return static::is_light_colour($this->value);
330
    }
331
332
    public function getNice()
333
    {
334
        $html = '
335
            <div
336
            style="
337
                width: 40px;
338
                height: 40px;
339
                border-radius: 40px;
340
                background-color: ' . $this->getBackgroundColour() . '!important;
341
                color: ' . $this->getFontColour() . '!important;
342
                border: 1px solid ' . $this->getFontColour() . '!important;
343
                text-align: center;
344
                display: table-cell;
345
                display: flex;
346
                flex-direction: column;
347
                justify-content: center;
348
            "
349
            >Aa</div> ';
350
351
        return DBField::create_field('HTMLText', $html);
352
    }
353
354
    public function getIsDarkColour(): bool
355
    {
356
        return static::is_light_colour($this->value) ? false : true;
357
    }
358
359
    public function getFontColour(): string
360
    {
361
        if (self::IS_BG_COLOUR) {
362
            return (string) self::get_font_colour($this->value);
363
        }
364
365
        return (string) $this->value;
366
    }
367
368
    public function getBackgroundColour(): string
369
    {
370
        if (self::IS_BG_COLOUR) {
371
            return (string) $this->value;
372
        }
373
374
        return (string) self::get_font_colour($this->value);
375
    }
376
377
    public function getColours(): array
378
    {
379
        return $this->Config()->get('colours');
380
    }
381
382
    protected function getMyColours(): array
383
    {
384
        $colours = $this->getColours();
385
386
        return empty($colours) ? static::DEFAULT_COLOURS : $colours;
387
    }
388
389
    protected function getRelatedColours(): array
390
    {
391
        $relatedColoursForAllColours = Config::inst()->get(static::class, 'linked_colours');
392
393
        return $relatedColoursForAllColours[$this->value] ?? [];
394
    }
395
396
    protected static function get_colour_as_db_field(string $colour, ?string $name = '')
397
    {
398
        $cacheKey = $colour . '_' . $name . '_' . static::class;
399
        if (! $colour || ! isset(static::$object_cache[$cacheKey])) {
400
            static::$object_cache[$cacheKey] = DBField::create_field(static::class, $colour, $name);
401
        }
402
403
        return static::$object_cache[$cacheKey];
404
    }
405
406
    protected function kebabCase(string $string)
407
    {
408
        return strtolower(preg_replace('/(?<!^)[A-Z]/', '-$0', $string));
409
    }
410
411
    private function classCleanup(string $name): string
412
    {
413
        $name = str_replace('#', '', $name);
414
        $name = preg_replace('#[^A-Za-z0-9]#', '-', $name);
415
416
        return static::CSS_CLASS_PREFIX . '-' . trim(trim(strtolower($name), '-'));
417
    }
418
}
419