Skip to content

Commit 40ed9e3

Browse files
committed
🚧 add more components and docs
1 parent 262401f commit 40ed9e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3209
-401
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Setup Node.js
15+
uses: actions/setup-node@v4
16+
with:
17+
node-version: '20'
18+
19+
- name: Install dependencies
20+
run: npm install
21+
22+
- name: Build documentation
23+
run: |
24+
# Add any build steps here if needed
25+
echo "Documentation is ready in index.html"
26+
27+
- name: Deploy to GitHub Pages
28+
uses: peaceiris/actions-gh-pages@v3
29+
with:
30+
github_token: ${{ secrets.GITHUB_TOKEN }}
31+
publish_dir: .
32+
publish_branch: gh-pages
33+
force_orphan: true

‎.junie/guidelines.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,16 @@ There is no build step or package manager required. The components are provided
2828
1. **No Shadow DOM**: These components **must not** use Shadow DOM. This ensures they can inherit global Bootstrap styles and interact correctly with Bootstrap's JavaScript plugins.
2929
2. **Deferred Rendering**: Most components use `setTimeout(() => { this._render(); }, 0)` in their `connectedCallback`. This is a critical pattern used to ensure that the browser has fully parsed the custom element's children before the `_render` method attempts to manipulate them.
3030
3. **Bootstrap Plugin Wrapping**: Components often serve as wrappers for Bootstrap's JS plugins (e.g., `bootstrap.Modal`, `bootstrap.Collapse`). They should handle the initialization and disposal of these plugins in `connectedCallback`/`_render` and `disconnectedCallback`.
31+
4. **Attribute vs Slot**: Components typically support both attributes for simple text (e.g., `title="My Modal"`) and slots for complex HTML content (e.g., `<div slot="title">...</div>`). Slots are simulated by manually moving children during the `_render` phase since Shadow DOM is not used.
32+
5. **Idempotency**: Components should be idempotent, meaning they should behave the same way regardless of how many times they are connected or disconnected. This is crucial for seamless integration with frameworks that may disconnect and reconnect components frequently.
3133

3234
### Code Style & Patterns
3335

3436
- **Naming Convention**: Custom elements are prefixed with `bs-` (e.g., `<bs-modal>`, `<bs-alert>`) when they are direct wrappers for Bootstrap components. When they do something in addition to a bootstrap component, they are named for what they do (e.g. `<loading-button>`).
3537
- **Attribute vs Slot**: Components typically support both attributes for simple text (e.g., `title="My Modal"`) and slots for complex HTML content (e.g., `<div slot="title">...</div>`). Slots are simulated by manually moving children during the `_render` phase since Shadow DOM is not used.
3638
- **Idempotency**: `_render` methods should check for an `_initialized` flag to prevent multiple renders if `connectedCallback` is triggered more than once.
3739
- **Cleanup**: Always implement `disconnectedCallback` to call `.dispose()` on any underlying Bootstrap plugin instances to prevent memory leaks.
40+
- **Encapsulation**: Bootstrap CSS expects that components are a `div`, so every custom component should wrap its content in a `<div>`. Copy any classes / attributes from the original element to the wrapper.
3841

3942
### Debugging
4043
- Check the browser console for any errors related to `bootstrap` being undefined.

‎README.md‎

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,96 @@
1-
# webcomponents
2-
Web components that I use across projects. All of these components are expecting that you are using the latest
3-
version of Bootstrap 5 for styling. If you are not, you may need to adjust the styling to match your project's
4-
CSS framework or create custom styles for the components.
5-
6-
## Available Components
7-
8-
- `<bs-modal>` (modal.js)
9-
- `<bs-accordion>` & `<bs-accordion-item>` (accordion.js)
10-
- `<bs-card>` (card.js)
11-
- `<bs-alert>` (alert.js)
12-
- `<bs-badge>` (badge.js)
13-
- `<bs-carousel>` & `<bs-carousel-item>` (carousel.js)
14-
- `<bs-tabs>` & `<bs-tab>` (navs.js)
15-
- `<bs-navbar>`, `<bs-navbar-brand>`, `<bs-navbar-nav>`, `<bs-nav-link>`, `<bs-nav-dropdown>`, `<bs-dropdown-item>`, `<bs-dropdown-divider>`, `<bs-navbar-text>` (navbar.js)
16-
- `<loading-button>` (loading_button.js)
17-
- `<bs-pagination>` & `<bs-pagination-item>` (pagination.js)
18-
- `<bs-placeholder>` (placeholder.js)
19-
- `<bs-spinner>` (spinner.js)
20-
- `<bs-toast>` (toast.js)
21-
- `<bs-tooltip>` (tooltip.js)
1+
# Bootstrap Web Components
2+
3+
Web components that wrap Bootstrap 5 functionality in custom elements. All components expect Bootstrap 5 for styling.
4+
5+
## Documentation
6+
7+
A comprehensive documentation page is available in `index.html` that includes:
8+
9+
- **Live demos** of all components
10+
- **Interactive navigation** with smooth scrolling
11+
- **Theme toggle** for light/dark mode
12+
- **Responsive design** for all screen sizes
13+
14+
Open `index.html` in your browser or deploy to GitHub Pages.
15+
16+
## 📦 Available Components
17+
18+
| Component | Custom Element | Description |
19+
|-----------|----------------|-------------|
20+
| Accordion | `<bs-accordion>` & `<bs-accordion-item>` | Expandable content panels |
21+
| Alert | `<bs-alert>` | Contextual feedback messages |
22+
| Badge | `<bs-badge>` | Small count and labeling component |
23+
| Card | `<bs-card>` | Flexible content container |
24+
| Carousel | `<bs-carousel>` & `<bs-carousel-item>` | Slideshow component |
25+
| Loading Button | `<loading-button>` | Button with loading state |
26+
| Modal | `<bs-modal>` | Dialog boxes |
27+
| Nav Tabs | `<bs-tabs>` & `<bs-tab>` | Tabbed navigation |
28+
| Navbar | `<bs-navbar>`, `<bs-navbar-brand>`, etc. | Navigation header |
29+
| Pagination | `<bs-pagination>` & `<bs-pagination-item>` | Page navigation |
30+
| Placeholder | `<bs-placeholder>` | Content loading placeholder |
31+
| Sidebar | `<bs-sidebar>` & `<bs-permanent-sidebar>` | Offcanvas and permanent sidebars |
32+
| Spinner | `<bs-spinner>` | Loading indicator |
33+
| Theme Toggle | `<theme-toggle>` | Light/dark mode switcher |
34+
| Toast | `<bs-toast>` | Push notifications |
35+
| Tooltip | `<bs-tooltip>` | Hover information |
36+
37+
## Usage
38+
39+
### Basic Usage
40+
41+
1. Include Bootstrap 5 CSS and JS
42+
2. Include the component JavaScript file
43+
3. Use the custom element in your HTML
44+
45+
### Example
46+
47+
```html
48+
<!DOCTYPE html>
49+
<html>
50+
<head>
51+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
52+
</head>
53+
<body>
54+
<!-- Include component -->
55+
<script src="components/alert/alert.js"></script>
56+
57+
<!-- Use component -->
58+
<bs-alert type="success">
59+
Hello from a web component!
60+
</bs-alert>
61+
62+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
63+
</body>
64+
</html>
65+
```
66+
67+
## Development
68+
69+
### Requirements
70+
71+
- Bootstrap 5.3.3 (CSS and JS)
72+
- Bootstrap Icons 1.11.3 (optional, for icons)
73+
74+
### Structure
75+
76+
```
77+
components/
78+
├── component-name/
79+
│ ├── component-name.js # Web Component implementation
80+
│ └── component-name_example.html # Example usage
81+
├── index.html # Main documentation
82+
└── README.md # Project documentation
83+
```
84+
85+
## License
86+
87+
MIT License - see [LICENSE](LICENSE) file for details.
88+
89+
## Contributing
90+
91+
Contributions welcome! Open an issue or submit a pull request.
92+
93+
## Links
94+
95+
- [Bootstrap Documentation](https://getbootstrap.com/docs/5.3/)
96+
- [Web Components MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components)

‎components/accordion/accordion.js‎

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,35 @@ class BsAccordion extends HTMLElement {
2525
this.id = `accordion-${Math.random().toString(36).substr(2, 9)}`;
2626
}
2727

28-
this.classList.add('accordion');
28+
setTimeout(() => {
29+
this._render();
30+
}, 0);
31+
}
32+
33+
_render() {
34+
if (this._initialized) return;
35+
this._initialized = true;
36+
37+
this.style.display = 'block';
38+
39+
const container = document.createElement('div');
40+
container.classList.add('accordion');
2941
if (this.hasAttribute('flush')) {
30-
this.classList.add('accordion-flush');
42+
container.classList.add('accordion-flush');
43+
}
44+
45+
// Pass through classes from the host element to the underlying div
46+
const hostClasses = this.getAttribute('class');
47+
if (hostClasses) {
48+
container.className += ` ${hostClasses}`;
49+
}
50+
51+
// Move children to the container
52+
while (this.firstChild) {
53+
container.appendChild(this.firstChild);
3154
}
3255

33-
this._initialized = true;
56+
this.appendChild(container);
3457
}
3558
}
3659

@@ -80,6 +103,8 @@ class BsAccordionItem extends HTMLElement {
80103
if (this._initialized) return;
81104
this._initialized = true;
82105

106+
this.style.display = 'block';
107+
83108
const title = this.getAttribute('title') || '';
84109
const expanded = this.hasAttribute('expanded');
85110

@@ -99,31 +124,41 @@ class BsAccordionItem extends HTMLElement {
99124
const itemId = this.id || `accordion-item-${Math.random().toString(36).substr(2, 9)}`;
100125
const collapseId = `collapse-${itemId}`;
101126

102-
this.classList.add('accordion-item');
103-
104127
// Capture children and clear innerHTML for the new structure
105128
const fragment = document.createDocumentFragment();
106129
while (this.firstChild) {
107130
fragment.appendChild(this.firstChild);
108131
}
109132

110-
this.innerHTML = `
111-
<h2 class="accordion-header">
112-
<button class="accordion-button ${expanded ? '' : 'collapsed'}" type="button"
113-
data-bs-toggle="collapse" data-bs-target="#${collapseId}"
114-
aria-expanded="${expanded}" aria-controls="${collapseId}">
115-
${title}
116-
</button>
117-
</h2>
118-
<div id="${collapseId}" class="accordion-collapse collapse ${expanded ? 'show' : ''}"
119-
${parentId ? `data-bs-parent="#${parentId}"` : ''}>
120-
<div class="accordion-body">
133+
const itemElement = document.createElement('div');
134+
itemElement.className = 'accordion-item';
135+
136+
// Pass through classes from the host element to the underlying div
137+
const hostClasses = this.getAttribute('class');
138+
if (hostClasses) {
139+
itemElement.className += ` ${hostClasses}`;
140+
}
141+
142+
itemElement.innerHTML = `
143+
<h2 class="accordion-header">
144+
<button class="accordion-button ${expanded ? '' : 'collapsed'}" type="button"
145+
data-bs-toggle="collapse" data-bs-target="#${collapseId}"
146+
aria-expanded="${expanded}" aria-controls="${collapseId}">
147+
${title}
148+
</button>
149+
</h2>
150+
<div id="${collapseId}" class="accordion-collapse collapse ${expanded ? 'show' : ''}"
151+
${parentId ? `data-bs-parent="#${parentId}"` : ''}>
152+
<div class="accordion-body">
153+
</div>
121154
</div>
122-
</div>
123155
`;
124156

125-
const bodyContainer = this.querySelector('.accordion-body');
126-
const headerButton = this.querySelector('.accordion-button');
157+
this.innerHTML = '';
158+
this.appendChild(itemElement);
159+
160+
const bodyContainer = itemElement.querySelector('.accordion-body');
161+
const headerButton = itemElement.querySelector('.accordion-button');
127162

128163
// Distribute children to their respective slots
129164
Array.from(fragment.childNodes).forEach(child => {

‎components/accordion/accordion_example.html‎

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1">
66
<title>BsAccordion Web Component Demo</title>
77
<!-- Load Bootstrap CSS -->
8-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
8+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
99
<style>
1010
body { padding-bottom: 50px; }
1111
hr { margin: 2rem 0; }
12+
.example-container { max-width: 800px; margin: 0 auto; }
1213
</style>
1314
</head>
1415
<body>
15-
<div class="container mt-5">
16+
<div class="container mt-5 example-container">
1617
<h1>BsAccordion Web Component Demo</h1>
1718
<p class="lead">Examples of the <code>&lt;bs-accordion&gt;</code> and <code>&lt;bs-accordion-item&gt;</code> custom elements.</p>
1819

@@ -31,6 +32,19 @@ <h3>1. Basic Accordion</h3>
3132
This is the third item's body.
3233
</bs-accordion-item>
3334
</bs-accordion>
35+
<bs-code-block class="mt-2">
36+
&lt;bs-accordion id="basicAccordion"&gt;
37+
&lt;bs-accordion-item title="Accordion Item #1" expanded&gt;
38+
&lt;strong&gt;This is the first item's body.&lt;/strong&gt; It is shown by default, because of the &lt;code&gt;expanded&lt;/code&gt; attribute.
39+
&lt;/bs-accordion-item&gt;
40+
&lt;bs-accordion-item title="Accordion Item #2"&gt;
41+
This is the second item's body. Opening this will close the first one.
42+
&lt;/bs-accordion-item&gt;
43+
&lt;bs-accordion-item title="Accordion Item #3"&gt;
44+
This is the third item's body.
45+
&lt;/bs-accordion-item&gt;
46+
&lt;/bs-accordion&gt;
47+
</bs-code-block>
3448
</section>
3549

3650
<hr>
@@ -47,6 +61,16 @@ <h3>2. Flush Accordion</h3>
4761
Placeholder content for the second flush item.
4862
</bs-accordion-item>
4963
</bs-accordion>
64+
<bs-code-block class="mt-2">
65+
&lt;bs-accordion flush&gt;
66+
&lt;bs-accordion-item title="Flush Item #1"&gt;
67+
Placeholder content for the first flush item.
68+
&lt;/bs-accordion-item&gt;
69+
&lt;bs-accordion-item title="Flush Item #2"&gt;
70+
Placeholder content for the second flush item.
71+
&lt;/bs-accordion-item&gt;
72+
&lt;/bs-accordion&gt;
73+
</bs-code-block>
5074
</section>
5175

5276
<hr>
@@ -63,6 +87,16 @@ <h3>3. Always Open Accordion</h3>
6387
Go ahead, open me! The first one won't close.
6488
</bs-accordion-item>
6589
</bs-accordion>
90+
<bs-code-block class="mt-2">
91+
&lt;bs-accordion always-open&gt;
92+
&lt;bs-accordion-item title="Always Open #1" expanded&gt;
93+
This item is open and will stay open even if you open the next one.
94+
&lt;/bs-accordion-item&gt;
95+
&lt;bs-accordion-item title="Always Open #2"&gt;
96+
Go ahead, open me! The first one won't close.
97+
&lt;/bs-accordion-item&gt;
98+
&lt;/bs-accordion&gt;
99+
</bs-code-block>
66100
</section>
67101

68102
<hr>
@@ -82,6 +116,19 @@ <h3>4. Custom Header Slot</h3>
82116
This one uses the simple <code>title</code> attribute.
83117
</bs-accordion-item>
84118
</bs-accordion>
119+
<bs-code-block class="mt-2">
120+
&lt;bs-accordion&gt;
121+
&lt;bs-accordion-item&gt;
122+
&lt;span slot="header" class="text-primary"&gt;
123+
&lt;i class="bi bi-star-fill"&gt;&lt;/i&gt; &lt;strong&gt;Custom Header&lt;/strong&gt; with Icon
124+
&lt;/span&gt;
125+
You can put any HTML in the header using the &lt;code&gt;slot="header"&lt;/code&gt;.
126+
&lt;/bs-accordion-item&gt;
127+
&lt;bs-accordion-item title="Regular Header"&gt;
128+
This one uses the simple &lt;code&gt;title&lt;/code&gt; attribute.
129+
&lt;/bs-accordion-item&gt;
130+
&lt;/bs-accordion&gt;
131+
</bs-code-block>
85132
</section>
86133

87134
<hr>
@@ -101,14 +148,28 @@ <h3>5. Programmatic Control</h3>
101148
I am being controlled by the buttons above.
102149
</bs-accordion-item>
103150
</bs-accordion>
151+
<bs-code-block class="mt-2">
152+
&lt;div class="mb-3"&gt;
153+
&lt;button class="btn btn-sm btn-outline-primary" onclick="document.getElementById('progItem').show()"&gt;Show Item&lt;/button&gt;
154+
&lt;button class="btn btn-sm btn-outline-secondary" onclick="document.getElementById('progItem').hide()"&gt;Hide Item&lt;/button&gt;
155+
&lt;button class="btn btn-sm btn-outline-info" onclick="document.getElementById('progItem').toggle()"&gt;Toggle Item&lt;/button&gt;
156+
&lt;/div&gt;
157+
158+
&lt;bs-accordion&gt;
159+
&lt;bs-accordion-item id="progItem" title="Controlled Item"&gt;
160+
I am being controlled by the buttons above.
161+
&lt;/bs-accordion-item&gt;
162+
&lt;/bs-accordion&gt;
163+
</bs-code-block>
104164
</section>
105165
</div>
106166

107167
<!-- Bootstrap Icons (for the custom header example) -->
108168
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
109169
<!-- Load Bootstrap JS Bundle -->
110-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
170+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
111171
<!-- Load our Web Component -->
112172
<script src="accordion.js"></script>
173+
<script src="../code_block/code_block.js"></script>
113174
</body>
114175
</html>

0 commit comments

Comments
 (0)