rest_framework 0.8.15 → 0.8.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ ---
2
+ layout: default
3
+ title: Serializers
4
+ position: 3
5
+ slug: serializers
6
+ ---
7
+ # Serializers
8
+
9
+ Serializers allow complex objects to be converted to Ruby primitives (`Array` and `Hash` objects),
10
+ which can then be converted to JSON or XML.
11
+
12
+ ## NativeSerializer
13
+
14
+ This serializer uses Rails' native `ActiveModel::Serializers.serializable_hash` method to convert
15
+ records/recordsets to Ruby primitives (`Array` and `Hash`).
16
+
17
+ This is the default serializer, you can configure it using the controller class attributes
18
+ `native_serializer_config` (or `native_serializer_singular_config` /
19
+ `native_serializer_plural_config`):
20
+
21
+ ```ruby
22
+ class Api::MoviesController < ApiController
23
+ include RESTFramework::ModelControllerMixin
24
+
25
+ self.native_serializer_config = {
26
+ only: [:id, :name],
27
+ methods: [:active, :some_expensive_computed_property],
28
+ include: {cast_members: { only: [:id, :name] }},
29
+ }
30
+ end
31
+ ```
32
+
33
+ If you want to re-use a serializer, then you can define it as a standalone class, and you can even
34
+ nest them. You can also define separate configurations for serializing individual records vs
35
+ recordsets using `singular_config` and `plural_config`, respectively.
36
+
37
+ ```ruby
38
+ class Api::MoviesController < ApiController
39
+ include RESTFramework::ModelControllerMixin
40
+
41
+ class CastMemberSerializer < RESTFramework::NativeSerializer
42
+ self.config = { only: [:id, :name], methods: [:net_worth] }
43
+ self.plural_config = { only: [:id, :name] }
44
+ end
45
+
46
+ class MovieSerializer < RESTFramework::NativeSerializer
47
+ self.config = {
48
+ only: [:id, :name],
49
+ include: {cast_members: CastMemberSerializer.new(many: true)},
50
+ }
51
+ self.singular_config = {
52
+ only: [:id, :name],
53
+ methods: [:active, :some_expensive_computed_property],
54
+ include: {cast_members: CastMemberSerializer.new(many: true)},
55
+ }
56
+ end
57
+
58
+ self.serializer_class = MovieSerializer
59
+ end
60
+ ```
@@ -0,0 +1,41 @@
1
+ ---
2
+ layout: default
3
+ title: Filtering / Ordering
4
+ position: 4
5
+ slug: filtering-and-ordering
6
+ ---
7
+ # Filtering and Ordering
8
+
9
+ While you can control the recordset that the API exposes, sometimes you want the user to control the
10
+ records they want to see, or the order of those records. Both filtering and ordering are
11
+ accomplished through what we call filters. To control the filter backends that a controller uses,
12
+ you can either adjust the `filter_backends` controller attribute or you can override the
13
+ `get_filter_backends()` method.
14
+
15
+ ## `ModelFilter`
16
+
17
+ This filter provides basic user-controllable filtering of the recordset using query params. For
18
+ example, a request to `/api/movies?cool=true` could return movies where `cool` is `true`.
19
+
20
+ If you include `ModelControllerMixin` into your controller, `ModelFilter` is included in the filter
21
+ backends by default.
22
+
23
+ ## `ModelOrderingFilter`
24
+
25
+ This filter provides basic user-controllable ordering of the recordset using query params. For
26
+ example, a request to `/api/movies?ordering=name` could order the movies by `name` rather than `id`.
27
+ `ordering=-name` would invert the ordering. You can also order with multiple parameters with a comma
28
+ separated list, like: `ordering=director,-name`.
29
+
30
+ If you include `ModelControllerMixin` into your controller, `ModelOrderingFilter` is included in the
31
+ filter backends by default. You can use `ordering_fields` to controller which fields are allowed to
32
+ be ordered by. To adjust the parameter that the user passes, adjust `ordering_query_param`; the
33
+ default is `"ordering"`.
34
+
35
+ ## `ModelSearchFilter`
36
+
37
+ This filter provides basic user-controllable searching of the recordset using the `search` query
38
+ parameter (adjustable with the `search_query_param`). For example, a request to
39
+ `/api/movies?search=Star` could return movies where `name` contains the string `Star`. The search is
40
+ performed against the `search_fields` attribute, but if that is not set, then the search is
41
+ performed against a configurable default set of fields (`search_columns`).
@@ -0,0 +1,21 @@
1
+ ---
2
+ layout: default
3
+ title: Pagination
4
+ position: 5
5
+ slug: pagination
6
+ ---
7
+ # Pagination
8
+
9
+ For large result sets, you may need to provide pagination. You can configure the paginator for a
10
+ controller by setting the `paginator_class` to the paginator you want to use.
11
+
12
+ ## PageNumberPaginator
13
+
14
+ This is a simple paginator which splits a recordset into pages and allows the user to select the
15
+ desired page using the `page` query parameter (e.g., `/api/movies?page=3`). To adjust this query
16
+ parameter, set the `page_query_param` controller attribute.
17
+
18
+ By default the user can adjust the page size using the `page_size` query param. To adjust this query
19
+ parameter, you can set the `page_size_query_param` controller attribute, or set it to `nil` to
20
+ disable this functionality. By default, there is no upper limit to the size of a page a user can
21
+ request. To enforce an upper limit, set the `max_page_size` controller attribute.
@@ -0,0 +1,144 @@
1
+ {% capture headingsWorkspace %}
2
+ {% comment %}
3
+ Copyright (c) 2018 Vladimir "allejo" Jimenez
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
25
+ {% endcomment %}
26
+ {% comment %}
27
+ Version 1.0.7
28
+ https://github.com/allejo/jekyll-anchor-headings
29
+
30
+ "Be the pull request you wish to see in the world." ~Ben Balter
31
+
32
+ Usage:
33
+ {% include anchor_headings.html html=content anchorBody="#" %}
34
+
35
+ Parameters:
36
+ * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
37
+
38
+ Optional Parameters:
39
+ * beforeHeading (bool) : false - Set to true if the anchor should be placed _before_ the heading's content
40
+ * anchorAttrs (string) : '' - Any custom HTML attributes that will be added to the `<a>` tag; you may NOT use `href`, `class` or `title`;
41
+ the `%heading%` and `%html_id%` placeholders are available
42
+ * anchorBody (string) : '' - The content that will be placed inside the anchor; the `%heading%` placeholder is available
43
+ * anchorClass (string) : '' - The class(es) that will be used for each anchor. Separate multiple classes with a space
44
+ * anchorTitle (string) : '' - The `title` attribute that will be used for anchors
45
+ * h_min (int) : 1 - The minimum header level to build an anchor for; any header lower than this value will be ignored
46
+ * h_max (int) : 6 - The maximum header level to build an anchor for; any header greater than this value will be ignored
47
+ * bodyPrefix (string) : '' - Anything that should be inserted inside of the heading tag _before_ its anchor and content
48
+ * bodySuffix (string) : '' - Anything that should be inserted inside of the heading tag _after_ its anchor and content
49
+
50
+ Output:
51
+ The original HTML with the addition of anchors inside of all of the h1-h6 headings.
52
+ {% endcomment %}
53
+
54
+ {% assign minHeader = include.h_min | default: 1 %}
55
+ {% assign maxHeader = include.h_max | default: 6 %}
56
+ {% assign beforeHeading = include.beforeHeading %}
57
+ {% assign nodes = include.html | split: '<h' %}
58
+
59
+ {% capture edited_headings %}{% endcapture %}
60
+
61
+ {% for _node in nodes %}
62
+ {% capture node %}{{ _node | strip }}{% endcapture %}
63
+
64
+ {% if node == "" %}
65
+ {% continue %}
66
+ {% endif %}
67
+
68
+ {% assign nextChar = node | replace: '"', '' | strip | slice: 0, 1 %}
69
+ {% assign headerLevel = nextChar | times: 1 %}
70
+
71
+ <!-- If the level is cast to 0, it means it's not a h1-h6 tag, so let's see if we need to fix it -->
72
+ {% if headerLevel == 0 %}
73
+ <!-- Split up the node based on closing angle brackets and get the first one. -->
74
+ {% assign firstChunk = node | split: '>' | first %}
75
+
76
+ <!-- If the first chunk does NOT contain a '<', that means we've broken another HTML tag that starts with 'h' -->
77
+ {% unless firstChunk contains '<' %}
78
+ {% capture node %}<h{{ node }}{% endcapture %}
79
+ {% endunless %}
80
+
81
+ {% capture edited_headings %}{{ edited_headings }}{{ node }}{% endcapture %}
82
+ {% continue %}
83
+ {% endif %}
84
+
85
+ {% capture _closingTag %}</h{{ headerLevel }}>{% endcapture %}
86
+ {% assign _workspace = node | split: _closingTag %}
87
+ {% assign _idWorkspace = _workspace[0] | split: 'id="' %}
88
+ {% assign _idWorkspace = _idWorkspace[1] | split: '"' %}
89
+ {% assign html_id = _idWorkspace[0] %}
90
+
91
+ {% capture _hAttrToStrip %}{{ _workspace[0] | split: '>' | first }}>{% endcapture %}
92
+ {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
93
+
94
+ <!-- Build the anchor to inject for our heading -->
95
+ {% capture anchor %}{% endcapture %}
96
+
97
+ {% if html_id and headerLevel >= minHeader and headerLevel <= maxHeader %}
98
+ {% capture anchor %}href="#{{ html_id }}"{% endcapture %}
99
+
100
+ {% if include.anchorClass %}
101
+ {% capture anchor %}{{ anchor }} class="{{ include.anchorClass }}"{% endcapture %}
102
+ {% endif %}
103
+
104
+ {% if include.anchorTitle %}
105
+ {% capture anchor %}{{ anchor }} title="{{ include.anchorTitle | replace: '%heading%', header }}"{% endcapture %}
106
+ {% endif %}
107
+
108
+ {% if include.anchorAttrs %}
109
+ {% capture anchor %}{{ anchor }} {{ include.anchorAttrs | replace: '%heading%', header | replace: '%html_id%', html_id }}{% endcapture %}
110
+ {% endif %}
111
+
112
+ {% capture anchor %}<a {{ anchor }}>{{ include.anchorBody | replace: '%heading%', header | default: '' }}</a>{% endcapture %}
113
+
114
+ <!-- In order to prevent adding extra space after a heading, we'll let the 'anchor' value contain it -->
115
+ {% if beforeHeading %}
116
+ {% capture anchor %}{{ anchor }} {% endcapture %}
117
+ {% else %}
118
+ {% capture anchor %} {{ anchor }}{% endcapture %}
119
+ {% endif %}
120
+ {% endif %}
121
+
122
+ {% capture new_heading %}
123
+ <h{{ _hAttrToStrip }}
124
+ {{ include.bodyPrefix }}
125
+ {% if beforeHeading %}
126
+ {{ anchor }}{{ header }}
127
+ {% else %}
128
+ {{ header }}{{ anchor }}
129
+ {% endif %}
130
+ {{ include.bodySuffix }}
131
+ </h{{ headerLevel }}>
132
+ {% endcapture %}
133
+
134
+ <!--
135
+ If we have content after the `</hX>` tag, then we'll want to append that here so we don't lost any content.
136
+ -->
137
+ {% assign chunkCount = _workspace | size %}
138
+ {% if chunkCount > 1 %}
139
+ {% capture new_heading %}{{ new_heading }}{{ _workspace | last }}{% endcapture %}
140
+ {% endif %}
141
+
142
+ {% capture edited_headings %}{{ edited_headings }}{{ new_heading }}{% endcapture %}
143
+ {% endfor %}
144
+ {% endcapture %}{% assign headingsWorkspace = '' %}{{ edited_headings | strip }}
@@ -0,0 +1,35 @@
1
+ <head>
2
+ <meta charset="utf-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
4
+ <link rel='icon' type='image/x-icon' href='/assets/images/favicon.ico' />
5
+
6
+ <title>{% if page.title %}{{ page.title | escape }}{% else %}{{ site.title | escape }}{% endif %}</title>
7
+
8
+ <!-- Bootstrap -->
9
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
10
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/js/bootstrap.bundle.min.js" integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N" crossorigin="anonymous"></script>
11
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css">
12
+
13
+ <!-- Highlight.js -->
14
+ <link rel="stylesheet" class="rrf-light-mode" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-light.min.css" integrity="sha512-WDk6RzwygsN9KecRHAfm9HTN87LQjqdygDmkHSJxVkVI7ErCZ8ZWxP6T8RvBujY1n2/E4Ac+bn2ChXnp5rnnHA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
15
+ <link rel="stylesheet" class="rrf-dark-mode" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-dark.min.css" integrity="sha512-Vj6gPCk8EZlqnoveEyuGyYaWZ1+jyjMPg8g4shwyyNlRQl6d3L9At02ZHQr5K6s5duZl/+YKMnM3/8pDhoUphg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
16
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js" integrity="sha512-bgHRAiTjGrzHzLyKOnpFvaEpGzJet3z4tZnXGjpsCcqOnAH6VGUx9frc5bcIhKTVLEiCO6vEhNAgx5jtLUYrfA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
17
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/shell.min.js" integrity="sha512-X2JngetHwVsp0j3n6lo8HGdXQKLpz2hwFfQkG996OfanpFaQJFgjKJlmzsdefWsHTQIwY539tD09JF48kCPMXw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
18
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/erb.min.js" integrity="sha512-flbEiCcectGeyRXyuMZW5jlAGIQ1/qrTZS6DsZDTqObM0JG/isYHvUyehOyt14ssmY85gZRYra+IJR9+azRuqw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
19
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/ruby.min.js" integrity="sha512-xRUQANk9Iw3wtAp0cBOa1Ghr7yIFrMiJiEujrMGf04qOau23exxj4R7DLUeLGfLiDbVSK0FyN8v2ns4m/6iNmQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
20
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/json.min.js" integrity="sha512-0xYvyncS9OLE7GOpNBZFnwyh9+bq4HVgk4yVVYI678xRvE22ASicF1v6fZ1UiST+M6pn17MzFZdvVCI3jTHSyw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
21
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/xml.min.js" integrity="sha512-5zBcw+OKRkaNyvUEPlTSfYylVzgpi7KpncY36b0gRudfxIYIH0q0kl2j26uCUB3YBRM6ytQQEZSgRg+ZlBTmdA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
22
+
23
+ <link rel="stylesheet" href="{{ "/assets/css/rest_framework.css" }}">
24
+ <script src="{{ "/assets/js/rest_framework.js" }}"></script>
25
+
26
+ <!-- Global site tag (gtag.js) - Google Analytics -->
27
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-P2KRPNXQMT"></script>
28
+ <script>
29
+ window.dataLayer = window.dataLayer || [];
30
+ function gtag(){dataLayer.push(arguments);}
31
+ gtag('js', new Date());
32
+
33
+ gtag('config', 'G-P2KRPNXQMT');
34
+ </script>
35
+ </head>
@@ -0,0 +1,58 @@
1
+ <header>
2
+ <div class="w-100 m-0 p-0" id="rrfAccentBar"></div>
3
+ <nav class="navbar py-0 navbar-expand-md" data-bs-theme="dark">
4
+ <div class="container">
5
+
6
+ <span class="navbar-brand p-0">
7
+ <a href="/">
8
+ <h1 class="text-light font-weight-light m-0 p-0" style="font-size: 1em; ">{{ site.title }}</h1>
9
+ </a>
10
+ </span>
11
+
12
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent">
13
+ <span class="navbar-toggler-icon"></span>
14
+ </button>
15
+
16
+ <div class="collapse navbar-collapse" id="navbarSupportedContent">
17
+ <ul class="navbar-nav ms-auto">
18
+ <li class="nav-item">
19
+ <a class="nav-link{% if page.url == '/' %} active{% endif %}" href="/">Home</a>
20
+ </li>
21
+
22
+ <li class="nav-item dropdown">
23
+ <a class="nav-link dropdown-toggle {% if page.url contains 'guide' %} active{% endif %}" href="#" role="button" data-bs-toggle="dropdown">
24
+ Guide
25
+ </a>
26
+ <div class="rrf-mode dropdown-menu">
27
+ {% for section in site.guide %}
28
+ <a class="dropdown-item" href="{{ section.url }}">{{ section.title }}</a>
29
+ {% endfor %}
30
+ </div>
31
+ </li>
32
+
33
+ <li class="nav-item ps-2" id="rrfGithubAndModeWrapper">
34
+ <span id="rrfGithubComponent">
35
+ <script async defer src="https://buttons.github.io/buttons.js"></script>
36
+ <a class="github-button" href="https://github.com/gregschmit/rails-rest-framework" data-show-count="true" aria-label="Star gregschmit/rails-rest-framework on GitHub">Star</a>
37
+ </span>
38
+ <div class="dropdown ms-auto float-end" id="rrfModeComponent">
39
+ <button class="btn btn-dark dropdown-toggle rounded-0 bg-black" style="border-color: black" data-bs-toggle="dropdown"></button>
40
+ <div class="rrf-mode dropdown-menu dropdown-menu-end py-0 rounded-0" style="font-size: .8em; min-width: 0">
41
+ <button class="dropdown-item text-end" data-rrf-mode-value="system">
42
+ System<i class="bi bi-circle-half ms-2"></i>
43
+ </button>
44
+ <button class="dropdown-item text-end" data-rrf-mode-value="light">
45
+ Light<i class="bi bi-sun-fill ms-2"></i>
46
+ </button>
47
+ <button class="dropdown-item text-end" data-rrf-mode-value="dark">
48
+ Dark<i class="bi bi-moon-stars-fill ms-2"></i>
49
+ </button>
50
+ </div>
51
+ </div>
52
+ </li>
53
+ </ul>
54
+ </div>
55
+
56
+ </div>
57
+ </nav>
58
+ </header>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html class="rrf-mode">
3
+ {% include head.html %}
4
+ <body>
5
+ {% include header.html %}
6
+ <div class="container pb-3">
7
+ <div id="headersTable" class="headers-table float-md-end m-2"></div>
8
+ {% include anchor_headings.html html=content anchorBody="#" %}
9
+ </div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,159 @@
1
+ /********************************
2
+ * START OF LIB/DOCS COMMON CSS *
3
+ ********************************/
4
+
5
+ :root {
6
+ --rrf-red: #900;
7
+ --rrf-red-hover: #5f0c0c;
8
+ --rrf-light-red: #db2525;
9
+ --rrf-light-red-hover: #b80404;
10
+ }
11
+ #rrfAccentBar {
12
+ background-color: var(--rrf-red);
13
+ height: .3em;
14
+ }
15
+ header nav { background-color: black; }
16
+
17
+ /* Header adjustments. */
18
+ h1 { font-size: 2rem; }
19
+ h2 { font-size: 1.7rem; }
20
+ h3 { font-size: 1.5rem; }
21
+ h4 { font-size: 1.3rem; }
22
+ h5 { font-size: 1.1rem; }
23
+ h6 { font-size: 1rem; }
24
+ h1, h2, h3, h4, h5, h6 {
25
+ color: var(--rrf-red);
26
+ }
27
+ html[data-bs-theme="dark"] h1,
28
+ html[data-bs-theme="dark"] h2,
29
+ html[data-bs-theme="dark"] h3,
30
+ html[data-bs-theme="dark"] h4,
31
+ html[data-bs-theme="dark"] h5,
32
+ html[data-bs-theme="dark"] h6 {
33
+ color: var(--rrf-light-red);
34
+ }
35
+
36
+ /* Improve code and code blocks. */
37
+ pre code {
38
+ display: block;
39
+ overflow-x: auto;
40
+ }
41
+ code, .trix-content pre {
42
+ padding: .5em !important;
43
+ background-color: #eee !important;
44
+ border: 1px solid #aaa;
45
+ border-radius: 3px;
46
+ }
47
+ p code {
48
+ padding: .1em .3em !important;
49
+ }
50
+ html[data-bs-theme="dark"] code, html[data-bs-theme="dark"] .trix-content pre {
51
+ background-color: #2b2b2b !important;
52
+ }
53
+
54
+ /* Anchors */
55
+ a:not(.nav-link) {
56
+ text-decoration: none;
57
+ color: var(--rrf-red);
58
+ }
59
+ a:hover:not(.nav-link) {
60
+ text-decoration: underline;
61
+ color: var(--rrf-red-hover);
62
+ }
63
+ html[data-bs-theme="dark"] a:not(.nav-link) { color: var(--rrf-light-red); }
64
+ html[data-bs-theme="dark"] a:hover:not(.nav-link) { color: var(--rrf-light-red-hover); }
65
+
66
+ /******************************
67
+ * END OF LIB/DOCS COMMON CSS *
68
+ ******************************/
69
+
70
+ /* Header adjustments. */
71
+ h1, h2, h3, h4, h5, h6 {
72
+ width: 100%;
73
+ font-weight: normal;
74
+ margin-top: 1.8rem;
75
+ margin-bottom: 1.2rem;
76
+ }
77
+ h1 a:not(:hover),
78
+ h2 a:not(:hover),
79
+ h3 a:not(:hover),
80
+ h4 a:not(:hover),
81
+ h5 a:not(:hover),
82
+ h6 a:not(:hover) {
83
+ color: #ddd;
84
+ }
85
+ html[data-bs-theme="dark"] h1 a:not(:hover),
86
+ html[data-bs-theme="dark"] h2 a:not(:hover),
87
+ html[data-bs-theme="dark"] h3 a:not(:hover),
88
+ html[data-bs-theme="dark"] h4 a:not(:hover),
89
+ html[data-bs-theme="dark"] h5 a:not(:hover),
90
+ html[data-bs-theme="dark"] h6 a:not(:hover) {
91
+ color: #444;
92
+ }
93
+ h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover {
94
+ text-decoration: none !important;
95
+ }
96
+
97
+ /* Navbar */
98
+ .navbar .navbar-toggler {
99
+ margin: .2em 0;
100
+ padding: .2em .3em;
101
+ border: none;
102
+ }
103
+ .navbar .navbar-toggler .navbar-toggler-icon {
104
+ height: 1.1em;
105
+ width: 1.1em;
106
+ }
107
+ .navbar .navbar-nav .nav-item .nav-link {
108
+ padding: .45em .6em;
109
+ }
110
+ .navbar .navbar-nav .nav-item .nav-link:hover {
111
+ background-color: #262a2f;
112
+ }
113
+ .navbar .dropdown-menu a.dropdown-item {
114
+ font-size: .9em;
115
+ padding: .2em .8em;
116
+ }
117
+
118
+ /* Headers table. */
119
+ .headers-table {
120
+ padding: .5em 1em;
121
+ background-color: #eee;
122
+ border: 1px solid #aaa;
123
+ border-radius: .3em;
124
+ font-size: .9em;
125
+ }
126
+ html[data-bs-theme="dark"] .headers-table {
127
+ background-color: #2b2b2b;
128
+ }
129
+ .headers-table:empty { display: none; }
130
+ .headers-table ul {
131
+ list-style-type: none;
132
+ margin: 0;
133
+ padding-left: 0;
134
+ padding-right: .6em;
135
+ }
136
+ .headers-table ul li { margin: .3em 0; }
137
+ .headers-table ul ul { padding-left: .8em; padding-right: 0; }
138
+ .headers-table > ul > li {
139
+ font-weight: bold;
140
+ }
141
+
142
+ /* Style the github and mode component. */
143
+ #rrfGithubAndModeWrapper {
144
+ height: 2.4em;
145
+ }
146
+ #rrfGithubComponent {
147
+ display: inline-block;
148
+ position: relative;
149
+ top: .6em;
150
+ }
151
+ #rrfModeComponent .dropdown-toggle {
152
+ float: right;
153
+ }
154
+ #rrfModeComponent .dropdown-menu {
155
+ position: absolute;
156
+ right: 0;
157
+ left: auto;
158
+ top: 100%;
159
+ }
Binary file
@@ -0,0 +1,132 @@
1
+ /*******************************
2
+ * START OF LIB/DOCS COMMON JS *
3
+ *******************************/
4
+
5
+ ;(() => {
6
+ // Get the real mode from a selected mode. Anything other than "light" or "dark" is treated as
7
+ // "system" mode.
8
+ const rrfGetRealMode = (selectedMode) => {
9
+ if (selectedMode === "light" || selectedMode === "dark") {
10
+ return selectedMode
11
+ }
12
+
13
+ if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
14
+ return "dark"
15
+ }
16
+
17
+ return "light"
18
+ }
19
+
20
+ // Set the mode, given a "selected" mode.
21
+ const rrfSetSelectedMode = (selectedMode) => {
22
+ const modeComponent = document.getElementById("rrfModeComponent")
23
+
24
+ // Anything except "light" or "dark" is casted to "system".
25
+ if (selectedMode !== "light" && selectedMode !== "dark") {
26
+ selectedMode = "system"
27
+ }
28
+
29
+ // Store selected mode in `localStorage`.
30
+ localStorage.setItem("rrfMode", selectedMode)
31
+
32
+ // Set the mode selector to the selected mode.
33
+ let labelHTML
34
+ modeComponent.querySelectorAll("button[data-rrf-mode-value]").forEach((el) => {
35
+ if (el.getAttribute("data-rrf-mode-value") === selectedMode) {
36
+ el.classList.add("active")
37
+ labelHTML = el.querySelector("i").outerHTML.replace("ms-2", "me-1")
38
+ } else {
39
+ el.classList.remove("active")
40
+ }
41
+ })
42
+ modeComponent.querySelector("button[data-bs-toggle]").innerHTML = labelHTML
43
+
44
+ // Get the real mode to use.
45
+ realMode = rrfGetRealMode(selectedMode)
46
+
47
+ // Set the `realMode` effects.
48
+ if (realMode === "light") {
49
+ document.querySelectorAll(".rrf-light-mode").forEach((el) => {
50
+ el.disabled = false
51
+ })
52
+ document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
53
+ el.disabled = true
54
+ })
55
+ document.querySelectorAll(".rrf-mode").forEach((el) => {
56
+ el.setAttribute("data-bs-theme", "light")
57
+ })
58
+ } else if (realMode === "dark") {
59
+ document.querySelectorAll(".rrf-light-mode").forEach((el) => {
60
+ el.disabled = true
61
+ })
62
+ document.querySelectorAll(".rrf-dark-mode").forEach((el) => {
63
+ el.disabled = false
64
+ })
65
+ document.querySelectorAll(".rrf-mode").forEach((el) => {
66
+ el.setAttribute("data-bs-theme", "dark")
67
+ })
68
+ } else {
69
+ console.log(`RRF: Unknown mode: ${mode}`)
70
+ }
71
+ }
72
+
73
+ // Initialize dark/light mode.
74
+ document.addEventListener("DOMContentLoaded", (event) => {
75
+ const selectedMode = localStorage.getItem("rrfMode")
76
+ rrfSetSelectedMode(selectedMode)
77
+ document.querySelectorAll("#rrfModeComponent button[data-rrf-mode-value]").forEach((el) => {
78
+ el.addEventListener("click", (event) => {
79
+ rrfSetSelectedMode(event.target.getAttribute("data-rrf-mode-value"))
80
+ })
81
+ })
82
+ })
83
+
84
+ // Handle case where user changes system theme.
85
+ if (window.matchMedia) {
86
+ window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
87
+ const selectedMode = localStorage.getItem("rrfMode")
88
+ if (selectedMode !== "light" && selectedMode !== "dark") {
89
+ rrfSetSelectedMode("system")
90
+ }
91
+ })
92
+ }
93
+ })()
94
+
95
+ /*****************************
96
+ * END OF LIB/DOCS COMMON JS *
97
+ *****************************/
98
+
99
+ document.addEventListener("DOMContentLoaded", () => {
100
+ // Initialize `Highlight.js`.
101
+ hljs.configure({ ignoreUnescapedHTML: true })
102
+ hljs.highlightAll()
103
+
104
+ // Setup the floating table of contents.
105
+ let table = "<ul>"
106
+ let hlevel = 2
107
+ let hprevlevel = 2
108
+ document.querySelectorAll("h2, h3, h4").forEach((header) => {
109
+ hlevel = parseInt(header.tagName[1])
110
+
111
+ if (hlevel > hprevlevel) {
112
+ table += "<ul>"
113
+ } else if (hlevel < hprevlevel) {
114
+ Array(hprevlevel - hlevel)
115
+ .fill(0)
116
+ .forEach(function () {
117
+ table += "</ul>"
118
+ })
119
+ }
120
+ table += `<li><a href="${
121
+ header.querySelectorAll("a")[0].href
122
+ }">${header.childNodes[0].nodeValue.trim()}</a></li>`
123
+ hprevlevel = hlevel
124
+ })
125
+ if (hlevel > hprevlevel) {
126
+ table += "</ul>"
127
+ }
128
+ table += "</ul>"
129
+ if (table != "<ul></ul>") {
130
+ document.getElementById("headersTable").innerHTML = table
131
+ }
132
+ })