Compare commits
7 Commits
master
...
125-cratel
Author | SHA1 | Date | |
---|---|---|---|
84562f276a | |||
bf17a24704 | |||
c9ddc2513b | |||
e7f187c6c7 | |||
2dd5d2db1a | |||
729e4ea6fc | |||
f82f9334fd |
@ -1,8 +1,9 @@
|
||||
base_url = "https://m-labs.hk"
|
||||
base_url = "/"
|
||||
title = "M-Labs"
|
||||
description = "Open tools for open physics."
|
||||
|
||||
compile_sass = true
|
||||
insert_anchor_links = true
|
||||
build_search_index = false
|
||||
|
||||
[markdown]
|
||||
|
@ -44,7 +44,7 @@ M-Labs ships with UPS by default, and we also offer FedEx or DHL shipping for an
|
||||
##### Can I have a discount?
|
||||
|
||||
M-Labs offers discounts in the following cases:
|
||||
* 10% discount plus free DAP shipping if the research supported by our equipment is made public and exclusively published in a non-predatory scientific journal. A non-predatory journal is defined as an open access journal with an article processing fee of USD 650.00 or less.
|
||||
* 10% discount plus free DAP shipping if the research supported by our equipment is made public and exclusively published in a non-predatory scientific journal. A non-predatory journal is defined as an open access journal with an article processing fee of USD 500.00 or less.
|
||||
* Volume discounts (applies to new orders and not retroactively, volume is determined over the 365 days prior to the new order, the new order is included in the amount compared against the threshold):
|
||||
- 2% discount with an order volume exceeding USD 100,000.00.
|
||||
- 4% discount with an order volume exceeding USD 250,000.00.
|
||||
|
@ -37,7 +37,7 @@ The <a href="https://github.com/quartiq/stabilizer" target="_blank" rel="noopene
|
||||
|
||||
|
||||
|
||||
<div class="row d-flex align-items-top mt-5 mb-5">
|
||||
{% layout_html(css='row d-flex align-items-top mt-5 mb-5') %}
|
||||
|
||||
<div class="col-12 col-md-3">
|
||||
<ul>
|
||||
@ -66,7 +66,7 @@ The <a href="https://github.com/quartiq/stabilizer" target="_blank" rel="noopene
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% end %}
|
||||
|
||||
|
||||
|
||||
|
@ -13,25 +13,25 @@ template = "page.html"
|
||||
{% layout_card(title="ARTIQ manual", sameheight=100) %}
|
||||
<small>Stable version</small>
|
||||
|
||||
<a href="https://m-labs.hk/artiq/manual.pdf" target="_blank" itemprop="url">PDF</a> | <a href="https://m-labs.hk/artiq/manual/" target="_blank" rel="noopener noreferrer" itemprop="url">HTML</a>
|
||||
<a href="https://m-labs.hk/artiq/manual.pdf" target="_blank">PDF</a> | <a href="https://m-labs.hk/artiq/manual/" target="_blank" rel="noopener noreferrer">HTML</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="SiPyCo manual", sameheight=100) %}
|
||||
<small>Starting with ARTIQ-5, this library replaces <tt>artiq.protocols</tt>.</small>
|
||||
|
||||
<a href="https://m-labs.hk/artiq/sipyco-manual.pdf" target="_blank" itemprop="url">PDF</a> | <a href="https://m-labs.hk/artiq/sipyco-manual/" target="_blank" rel="noopener noreferrer" itemprop="url">HTML</a>
|
||||
<a href="https://m-labs.hk/artiq/sipyco-manual.pdf" target="_blank">PDF</a> | <a href="https://m-labs.hk/artiq/sipyco-manual/" target="_blank" rel="noopener noreferrer">HTML</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="ARTIQ manual", sameheight=100) %}
|
||||
<small>Beta (development) version</small>
|
||||
|
||||
<a href="https://m-labs.hk/artiq/manual-beta.pdf" target="_blank" itemprop="url">PDF</a> | <a href="https://m-labs.hk/artiq/manual-beta/" target="_blank" rel="noopener noreferrer" itemprop="url">HTML</a>
|
||||
<a href="https://m-labs.hk/artiq/manual-beta.pdf" target="_blank">PDF</a> | <a href="https://m-labs.hk/artiq/manual-beta/" target="_blank" rel="noopener noreferrer">HTML</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="ARTIQ manual", sameheight=100) %}
|
||||
<small>Legacy version</small>
|
||||
|
||||
<a href="https://m-labs.hk/artiq/manual-legacy.pdf" target="_blank" itemprop="url">PDF</a> | <a href="https://m-labs.hk/artiq/manual-legacy/" target="_blank" rel="noopener noreferrer" itemprop="url">HTML</a>
|
||||
<a href="https://m-labs.hk/artiq/manual-legacy.pdf" target="_blank">PDF</a> | <a href="https://m-labs.hk/artiq/manual-legacy/" target="_blank" rel="noopener noreferrer">HTML</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
@ -39,14 +39,14 @@ template = "page.html"
|
||||
{% layout_card(title="Slideshow", sameheight=100) %}
|
||||
<small>The ARTIQ experiment control system</small>
|
||||
|
||||
<a href="/docs/artiq/artiq_overview.pdf" target="_blank" rel="noopener noreferrer" itemprop="url">View slides</a>
|
||||
<a href="/docs/artiq/artiq_overview.pdf" target="_blank" rel="noopener noreferrer">View slides</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="Slideshow", sameheight=100) %}
|
||||
<small>Timing control in ARTIQ</small>
|
||||
|
||||
<a href="/docs/artiq/slides_timing.pdf" target="_blank" rel="noopener noreferrer" itemprop="url">View slides</a>
|
||||
<a href="/docs/artiq/slides_timing.pdf" target="_blank" rel="noopener noreferrer">View slides</a>
|
||||
{% end %}
|
||||
|
||||
</div>
|
||||
@ -64,40 +64,40 @@ template = "page.html"
|
||||
<div class="row mt-5">
|
||||
|
||||
{% layout_card(src="images/mattermost@2x.png", css="col-12 col-md-4 text-center") %}
|
||||
<a href="http://chat.m-labs.hk/" target="_blank" rel="noopener noreferrer" itemprop="url">Mattermost live chat</a>
|
||||
<a href="http://chat.m-labs.hk/" target="_blank" rel="noopener noreferrer">Mattermost live chat</a>
|
||||
|
||||
<small>(bridged to IRC)</small>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(src="images/forum@2x.png", css="col-12 col-md-4 text-center") %}
|
||||
<a href="https://forum.m-labs.hk/" target="_blank" rel="noopener noreferrer" itemprop="url">Forum</a>
|
||||
<a href="https://forum.m-labs.hk/" target="_blank" rel="noopener noreferrer">Forum</a>
|
||||
|
||||
<small> </small>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(src="images/irc@2x.png", css="col-12 col-md-4 text-center") %}
|
||||
<a href="https://webchat.oftc.net/" target="_blank" rel="noopener noreferrer" itemprop="url">IRC: #m-labs</a>
|
||||
<a href="https://webchat.oftc.net/" target="_blank" rel="noopener noreferrer">IRC: #m-labs</a>
|
||||
|
||||
<small>on OFTC</small>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(src="images/press@2x.png", css="col-12 col-md-4 text-center") %}
|
||||
<a href="https://www.nist.gov/news-events/news/2015/01/open-source-software-quantum-information" target="_blank" rel="noopener noreferrer" itemprop="url">NIST ARTIQ press release</a>
|
||||
<a href="https://www.nist.gov/news-events/news/2015/01/open-source-software-quantum-information" target="_blank" rel="noopener noreferrer">NIST ARTIQ press release</a>
|
||||
|
||||
<small> </small>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(src="images/git@2x.png", css="col-12 col-md-4 text-center") %}
|
||||
<a href="https://github.com/m-labs/artiq" target="_blank" rel="noopener noreferrer" itemprop="url">ARTIQ source code repository</a>
|
||||
<a href="https://github.com/m-labs/artiq" target="_blank" rel="noopener noreferrer">ARTIQ source code repository</a>
|
||||
|
||||
<small>on GitHub</small>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(src="images/git@2x.png", css="col-12 col-md-4 text-center") %}
|
||||
<a href="https://git.m-labs.hk" target="_blank" rel="noopener noreferrer" itemprop="url">Gitea</a>
|
||||
<a href="https://git.m-labs.hk" target="_blank" rel="noopener noreferrer">Gitea</a>
|
||||
|
||||
<small> </small>
|
||||
{% end %}
|
||||
@ -129,108 +129,102 @@ We welcome inquiries from research groups of all sizes.<br>[See what has been fu
|
||||
{% layout_card(title="Entangler core", sameheight=120) %}
|
||||
<small>A FPGA core written in Migen with ARTIQ interface, for controlling remote quantum entanglement of trapped ions.</small>
|
||||
|
||||
<a href="https://github.com/OxfordIonTrapGroup/entangler-core" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a> | <a href="https://arxiv.org/abs/1911.10841" target="_blank" rel="noopener noreferrer" itemprop="url">Paper</a>
|
||||
<a href="https://github.com/OxfordIonTrapGroup/entangler-core" target="_blank" rel="noopener noreferrer">Repository</a> | <a href="https://arxiv.org/abs/1911.10841" target="_blank" rel="noopener noreferrer">Paper</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="ndscan", sameheight=120) %}
|
||||
<small>N-dimensional scans for ARTIQ</small>
|
||||
|
||||
<a href="https://github.com/OxfordIonTrapGroup/ndscan" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/OxfordIonTrapGroup/ndscan" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="Oxford routines", sameheight=120) %}
|
||||
<small>Oxford Ion-Trap Group routines</small>
|
||||
|
||||
<a href="https://github.com/OxfordIonTrapGroup/oitg" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/OxfordIonTrapGroup/oitg" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="UCLA routines", sameheight=120) %}
|
||||
<small>ARTIQ experiments in use at UCLA AMO</small>
|
||||
|
||||
<a href="https://github.com/EGGS-Experiment/LAX_exp" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/EGGS-Experiment/LAX_exp" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="nvOS", sameheight=120) %}
|
||||
<small>A quantum operating system built around ARTIQ and NV centers in diamond.</small>
|
||||
|
||||
<a href="https://github.com/vontell/nvOS" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/vontell/nvOS" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="Haeffner Lab routines", sameheight=120) %}
|
||||
<small>Haeffner Lab (Berkeley) routines</small>
|
||||
|
||||
<a href="https://github.com/HaeffnerLab/artiq-work-lattice" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/HaeffnerLab/artiq-work-lattice" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="Birmingham examples", sameheight=120) %}
|
||||
<small>A repository of simple examples of ARTIQ code</small>
|
||||
|
||||
<a href="https://github.com/cnourshargh/Bham-ARTIQ-examples" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/cnourshargh/Bham-ARTIQ-examples" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="DAX - Duke ARTIQ extensions", sameheight=120) %}
|
||||
<small>A library to provide tools for system organization/abstraction and to improve usability by automating common functionality.</small>
|
||||
|
||||
<a href="https://gitlab.com/duke-artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repositories</a>
|
||||
<a href="https://gitlab.com/duke-artiq" target="_blank" rel="noopener noreferrer">Repositories</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="Argent", sameheight=120) %}
|
||||
<small>High-level sequence control interface for ARTIQ.</small>
|
||||
|
||||
<a href="https://github.com/robertfasano/argent" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/robertfasano/argent" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="flake8-artiq", sameheight=120) %}
|
||||
<small>A Flake8 plugin for checking ARTIQ code</small>
|
||||
|
||||
<a href="https://gitlab.com/duke-artiq/flake8-artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://gitlab.com/duke-artiq/flake8-artiq" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="GenericSCPIDriver", sameheight=120) %}
|
||||
<small>A generic Python driver for SCPI devices driven over serial connections. Compatible with ARTIQ.</small>
|
||||
|
||||
<a href="https://github.com/charlesbaynham/GenericSCPIDriver" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/charlesbaynham/GenericSCPIDriver" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="artiq_influx_generic", sameheight=120) %}
|
||||
<small>RPC interface to InfluxDB with support for generic data logging.</small>
|
||||
|
||||
<a href="https://gitlab.com/charlesbaynham/artiq_influx_generic" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://gitlab.com/charlesbaynham/artiq_influx_generic" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="DP700", sameheight=120) %}
|
||||
<small>ARTIQ NDSP for RIGOL DP700 Series power supplies.</small>
|
||||
|
||||
<a href="https://github.com/OregonIons/DP700" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/OregonIons/DP700" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="AGUC8", sameheight=120) %}
|
||||
<small>ARTIQ NDSP for Newport AG-UC8 piezo motor controller.</small>
|
||||
|
||||
<a href="https://github.com/OregonIons/AGUC8" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/OregonIons/AGUC8" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="TSICam", sameheight=120) %}
|
||||
<small>ARTIQ NDSP for Thorlabs Scientific Imaging cameras.</small>
|
||||
|
||||
<a href="https://github.com/OregonIons/TSICam" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/OregonIons/TSICam" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="U Toronto examples", sameheight=120) %}
|
||||
<small>A repository of examples of ARTIQ code and instructions</small>
|
||||
|
||||
<a href="https://github.com/vuthalab/artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="OffsetStabilizer", sameheight=120) %}
|
||||
<small>Stabilizer firmware for laser frequency offset stabilization</small>
|
||||
|
||||
<a href="https://github.com/PhBrb/OffsetStabilizer" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
<a href="https://github.com/vuthalab/artiq" target="_blank" rel="noopener noreferrer">Repository</a>
|
||||
{% end %}
|
||||
|
||||
</div>
|
||||
|
@ -28,10 +28,6 @@ One of the main devices in the Sinara family is the 1124 Carrier (codenamed Kasl
|
||||
|
||||
<a href="https://github.com/sinara-hw/Kasli/wiki" target="_blank" rel="noopener noreferrer">More information</a>
|
||||
|
||||
<p style="padding-top: 8px">
|
||||
<span class='doc-icon'></span> <a href="/docs/sinara-datasheets/1124.pdf" target="_blank" rel="noopener noreferrer">1124 Carrier Kasli 2.0 datasheet</a>
|
||||
</p>
|
||||
|
||||
{% end %}
|
||||
|
||||
{% layout_text_img(src="images/kasli-soc@2x.png", popup="images/origin/kasli-soc.jpg", alt="", textleft=true, shadow=false) %}
|
||||
@ -44,10 +40,6 @@ See our paper <a href="https://arxiv.org/abs/2111.15290" target="_blank" rel="no
|
||||
|
||||
<a href="https://github.com/sinara-hw/Kasli-SoC/wiki" target="_blank" rel="noopener noreferrer">More information</a>
|
||||
|
||||
<p style="padding-top: 8px">
|
||||
<span class='doc-icon'></span> <a href="/docs/sinara-datasheets/1125.pdf" target="_blank" rel="noopener noreferrer">1125 Carrier Kasli SoC datasheet</a>
|
||||
</p>
|
||||
|
||||
{% end %}
|
||||
|
||||
{% layout_text_img(src="images/isolated-ttl@2x.png", popup="images/origin/dio.jpg", alt="", shadow=false) %}
|
||||
@ -175,11 +167,6 @@ Comparing Mirny to Urukul:
|
||||
|
||||
<a href="https://github.com/sinara-hw/mirny/wiki" target="_blank" rel="noopener noreferrer">More information</a>
|
||||
|
||||
<p style="padding-top: 8px">
|
||||
<span class='doc-icon'></span> <a href="/docs/sinara-datasheets/4456.pdf" target="_blank" rel="noopener noreferrer">4456 Synthesizer Mirny datasheet</a>
|
||||
</p>
|
||||
|
||||
|
||||
{% end %}
|
||||
|
||||
|
||||
@ -246,11 +233,6 @@ In SU-Servo mode, the 5108 Sampler can be used in combination with the 4410 DDS
|
||||
|
||||
Note that update rate specification on this page is for the hardware only; ARTIQ kernel and RTIO overhead make the effective sample rate lower. Typically, only with gateware (e.g. SU-Servo) can the maximum bandwidth be achieved. SU-Servo is part of the regular ARTIQ firmware; development of other gateware can be purchased separately.
|
||||
|
||||
<p style="padding-top: 8px">
|
||||
<span class='doc-icon'></span> <a href="/docs/sinara-datasheets/5108.pdf" target="_blank" rel="noopener noreferrer">5108 ADC Sampler datasheet</a>
|
||||
</p>
|
||||
|
||||
|
||||
{% end %}
|
||||
|
||||
|
||||
@ -277,11 +259,6 @@ The Sinara 7210 is a low-noise clock distribution module that can be used to dis
|
||||
|
||||
<a href="https://github.com/sinara-hw/Clocker/wiki" target="_blank" rel="noopener noreferrer">More information</a>
|
||||
|
||||
<p style="padding-top: 8px">
|
||||
<span class='doc-icon'></span> <a href="/docs/sinara-datasheets/7210.pdf" target="_blank" rel="noopener noreferrer">7210 Clocker datasheet</a>
|
||||
</p>
|
||||
|
||||
|
||||
{% end %}
|
||||
|
||||
|
||||
|
859
package-lock.json
generated
859
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,7 @@
|
||||
"start": "npx webpack --watch",
|
||||
"build": "npx webpack",
|
||||
"start-dev": "npx webpack --watch --mode=development --devtool=inline-source-map",
|
||||
"build-dev": "npx webpack --mode=development --devtool=inline-source-map",
|
||||
"optimize-bootstrap": "zola build -o tmp_out && npx purgecss --css static/css/bootstrap-5.3.0.min.css --content 'tmp_out/**/*.html' -o static/css/bootstrap-5.3.0.opt.css --safelist modal-backdrop && rm -rf tmp_out"
|
||||
"build-dev": "npx webpack --mode=development --devtool=inline-source-map"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -34,8 +33,7 @@
|
||||
"babel-preset-minify": "^0.5.2",
|
||||
"webpack": "^5.93.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-preprocessor-loader": "^1.3.0",
|
||||
"purgecss": "^7.0.2"
|
||||
"webpack-preprocessor-loader": "^1.3.0"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
|
@ -107,14 +107,10 @@ h3, h2, h1 {
|
||||
padding: .4rem; }
|
||||
|
||||
|
||||
p ~ h5, div.desc-wrapper ~ h5 {
|
||||
p ~ h5 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
div.desc-wrapper {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
img.kf25 {
|
||||
width: 310px;
|
||||
}
|
||||
|
@ -117,10 +117,20 @@ button {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
height: 400px;
|
||||
align-self: center;
|
||||
border: 0;
|
||||
cursor: grab;
|
||||
&.default-icon {
|
||||
height: 144px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
@ -484,6 +494,45 @@ button {
|
||||
background-color: #ebebeb;
|
||||
padding: 5px 5px 12px;
|
||||
position: relative;
|
||||
|
||||
&.horizontal {
|
||||
flex-direction: column;
|
||||
min-height: 10px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 5px 3px;
|
||||
}
|
||||
.progress-container {
|
||||
max-width: 40px;
|
||||
padding: 0 10px;
|
||||
justify-self: start;
|
||||
}
|
||||
.product-name {
|
||||
justify-self: start;
|
||||
padding: 0 10px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.removeHorizontal {
|
||||
justify-self: end;
|
||||
padding: 0 10px;
|
||||
img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
@ -571,67 +620,6 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.overlayVariant {
|
||||
top: 24px;
|
||||
width: 140px;
|
||||
min-height: 40px;
|
||||
max-height: 320px;
|
||||
overflow-y: scroll;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
text-align: left;
|
||||
background-color: white;
|
||||
color: black;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
padding: 0.2rem 0;
|
||||
|
||||
p {
|
||||
font-size: .65rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
margin: 0.1rem 0.2rem;
|
||||
font-size: 0.75rem;
|
||||
|
||||
input {
|
||||
padding: 0;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.options-icon {
|
||||
display: inline;
|
||||
height: .875rem;
|
||||
margin-right: 0.2rem;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-check {
|
||||
min-height: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-smallcard {
|
||||
left: -38.5px; // (card width (63) - overlay width (140)) / 2
|
||||
|
||||
&.overlay-first {
|
||||
left: 0;
|
||||
}
|
||||
&.overlay-last {
|
||||
left: -67px;
|
||||
}
|
||||
}
|
||||
.overlay-bigcard {
|
||||
left: -7px; // (card width (126) - overlay width (140)) / 2
|
||||
}
|
||||
|
||||
.hovered {
|
||||
@ -778,3 +766,49 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.overlayVariant {
|
||||
width: 140px;
|
||||
min-height: 40px;
|
||||
max-height: 320px;
|
||||
overflow-y: scroll;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
text-align: left;
|
||||
background-color: white;
|
||||
color: black;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
padding: 0.2rem 0;
|
||||
|
||||
p {
|
||||
font-size: .65rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
margin: 0.1rem 0.2rem;
|
||||
font-size: 0.75rem;
|
||||
|
||||
input {
|
||||
padding: 0;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.options-icon {
|
||||
display: inline;
|
||||
height: .875rem;
|
||||
margin-right: 0.2rem;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-check {
|
||||
min-height: 1rem;
|
||||
}
|
||||
}
|
||||
|
5
static/css/bootstrap-5.3.0.opt.css
vendored
5
static/css/bootstrap-5.3.0.opt.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
static/images/shop/graphic-03_AFWS.svg
Normal file
1
static/images/shop/graphic-03_AFWS.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
1
static/images/shop/icon-drag-and-drop.svg
Normal file
1
static/images/shop/icon-drag-and-drop.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="256" height="256" version="1.1" viewBox="0 0 67.733 67.733" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1.2797 0 0 1.2797 -4.4788 -4.4788)" stroke-width="1.0001"><g fill-opacity="0" stroke="#fff" stroke-linecap="round" stroke-width="1.0001"><g stroke-linejoin="round" stroke-opacity=".8" stroke-width="1.0001"><path d="m4 28.965v2"/><path d="m4 40.948v2"/><path d="m4 16.983v2"/></g><g stroke-linejoin="round" stroke-opacity=".8" stroke-width="1.0001"><path d="m28.965 4h2"/><path d="m16.983 4h2"/><path d="m40.948 4.0001h2"/></g><g transform="translate(0 3.3081)" stroke-linejoin="round" stroke-opacity=".8" stroke-width="1.0001"><path d="m28.965 52.623h2"/><path d="m40.948 52.623h2"/><path d="m16.983 52.623h2"/></g><g transform="translate(3.3082)" stroke-linejoin="round" stroke-opacity=".8" stroke-width="1.0001"><path d="m52.623 28.965v2"/><path d="m52.623 16.983v2.0001"/><path d="m52.623 40.948v2.0001"/></g><g stroke-linejoin="round"><path d="m7 4h-3v3" stroke-opacity=".8" stroke-width="1.0001"/><path d="m52.931 4h3v3" stroke-opacity=".8" stroke-width="1.0001"/><path d="m7 55.931h-3v-3" stroke-opacity=".8" stroke-width="1.0001"/></g><path d="m28.311 27.311v2.0001m-1-1h2" stroke-opacity=".8" stroke-width="1.0001"/><path d="m52.931 55.931h3v-3" stroke-linejoin="round" stroke-opacity=".8" stroke-width="1.0001"/></g></g><g transform="matrix(.64211 -.68634 .59172 .63248 35.302 51.828)"><path transform="translate(1.6405e-6)" d="m7 13.5v-7c0-0.82842 0.67157-1.5 1.5-1.5s1.5 0.55521 1.5 1.3836v0.11637 2m0-1e-5v-2c0-2.2857 3-2.2857 3 0v0.52708 1.4729m0 3e-5v-1.4729c0-2.2857 3-2.2857 3 0v0.9015 0.57143m-3 0v-1.4729m0-0.52707v0.52707m3 1.4729v-0.57143-0.9015c0-2.2857-3-2.2857-3 0m3 1.4728v-0.57143c0-2.2857 3-2.2857 3 0m-12 2.5715-2.0041 2.6721c-0.57746 0.77-0.52464 1.842 0.12569 2.5515l3.7839 4.1279c0.37882 0.4132 0.91276 0.6485 1.4734 0.6485h4.6211c2.4 0 4-1.5 4-4v-8.5714" fill-opacity="0" stroke="#fff" stroke-opacity=".9"/></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
32
static/js/as.js
Normal file
32
static/js/as.js
Normal file
@ -0,0 +1,32 @@
|
||||
(function () {
|
||||
|
||||
var swRegistration;
|
||||
var newWorker;
|
||||
var isRefreshing = false;
|
||||
var pathname = window.location.pathname;
|
||||
var deferredPrompt;
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
console.info('Service Worker is supported');
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/sw.js', {
|
||||
updateViaCache: 'all',
|
||||
}).then(function (registration) {
|
||||
swRegistration = registration;
|
||||
console.log('[SW] registration successful with scope: ', swRegistration.scope);
|
||||
console.log('[SW] is waiting: ', swRegistration.waiting);
|
||||
|
||||
navigator.serviceWorker.addEventListener('controllerchange', function () {
|
||||
if (isRefreshing) {
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
isRefreshing = true;
|
||||
});
|
||||
}, function (err) {
|
||||
console.log('[SW] registration failed: ', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
2
static/js/jquery-3.7.0.min.js
vendored
Normal file
2
static/js/jquery-3.7.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
static/js/jquery-3.7.0.slim.min.js
vendored
Normal file
2
static/js/jquery-3.7.0.slim.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, {useRef, useEffect, useState} from 'react'
|
||||
import {Droppable} from "@hello-pangea/dnd";
|
||||
import {cartStyle, compareArraysWithIds} from "./utils";
|
||||
import {ProductCartItem} from "./ProductCartItem";
|
||||
@ -9,17 +9,17 @@ import {useShopStore} from "./shop_store";
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
/**
|
||||
* Component that displays a list of <ProductCartItem>
|
||||
*/
|
||||
export function Cart({crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
const containerRef = useRef(null);
|
||||
const [visiblePlaceholders, setVisiblePlaceholders] = useState(0);
|
||||
|
||||
const crate = useShopStore((state) => state.crates[crate_index], (a, b) => {
|
||||
return compareArraysWithIds(a.items, b.items) && a.occupiedHP === b.occupiedHP && a.crate_mode === b.crate_mode
|
||||
});
|
||||
const crateParams = useShopStore((state) => state.crateParams);
|
||||
const isCrate = useShopStore((state) => state.modes_order.includes(state.crates[crate_index].crate_mode));
|
||||
|
||||
// #!render_count
|
||||
console.log("Cart renders: ", renderCount)
|
||||
@ -27,23 +27,43 @@ export function Cart({crate_index}) {
|
||||
const nbrOccupied = hp_to_slots(crate.occupiedHP);
|
||||
const nbrSlots = hp_to_slots(crateParams(crate.crate_mode).hp);
|
||||
|
||||
useEffect(() => {
|
||||
const updateVisiblePlaceholders = () => {
|
||||
if (!containerRef.current) return;
|
||||
setVisiblePlaceholders(isCrate ?
|
||||
(nbrSlots - nbrOccupied) :
|
||||
// 10 is padding and 77 is the actual size with all the margins
|
||||
Math.max(1, Math.floor((containerRef.current.offsetWidth - 10) / 77) - nbrOccupied));
|
||||
};
|
||||
updateVisiblePlaceholders();
|
||||
const resizeObserver = new ResizeObserver(updateVisiblePlaceholders);
|
||||
if (containerRef.current) {
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}
|
||||
return () => {
|
||||
if (containerRef.current) {
|
||||
resizeObserver.unobserve(containerRef.current);
|
||||
}
|
||||
};
|
||||
}, [nbrOccupied, nbrSlots]);
|
||||
|
||||
const products = crate.items.map((item, index) => {
|
||||
return (
|
||||
<ProductCartItem
|
||||
card_index={index}
|
||||
crate_index={crate_index}
|
||||
first={index === 0}
|
||||
last={index === crate.items.length - 1 && nbrOccupied >= nbrSlots}
|
||||
key={item.id}/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Droppable droppableId={crate.id} direction="horizontal">
|
||||
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
ref={(element) => {
|
||||
containerRef.current = element;
|
||||
provided.innerRef(element);
|
||||
}}
|
||||
{...provided.droppableProps}
|
||||
style={cartStyle(
|
||||
provided.droppableProps.style,
|
||||
@ -60,11 +80,10 @@ export function Cart({crate_index}) {
|
||||
)}
|
||||
|
||||
<FakePlaceholder
|
||||
nToDraw={nbrSlots - nbrOccupied}
|
||||
nToDraw={visiblePlaceholders}
|
||||
isDraggingOver={snapshot.isDraggingOver}/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</Droppable>
|
||||
);
|
||||
}
|
59
static/js/shop/CartHorizontal.jsx
Normal file
59
static/js/shop/CartHorizontal.jsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React from 'react'
|
||||
import {Droppable} from "@hello-pangea/dnd";
|
||||
import {cartStyle, compareArraysWithIds} from "./utils";
|
||||
import {ProductCartItemHorizontal} from "./ProductCartItemHorizontal";
|
||||
import {HORIZONTAL_CART_MARKER, useShopStore} from "./shop_store";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
|
||||
/**
|
||||
* Component that displays a list of <ProductCartItem>
|
||||
*/
|
||||
export function CartHorizontal({crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const crate = useShopStore((state) => state.crates[crate_index], (a, b) => {
|
||||
return compareArraysWithIds(a.h_items, b.h_items)
|
||||
});
|
||||
|
||||
// #!render_count
|
||||
console.log("CartHorizontal renders: ", renderCount)
|
||||
|
||||
|
||||
const products = crate.h_items.map((item, index) => {
|
||||
return (
|
||||
<ProductCartItemHorizontal
|
||||
card_index={index}
|
||||
crate_index={crate_index}
|
||||
key={item.id}/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Droppable droppableId={crate.id+HORIZONTAL_CART_MARKER} direction="vertical">
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
style={cartStyle(
|
||||
provided.droppableProps.style,
|
||||
snapshot,
|
||||
)}
|
||||
className="items-cart-list horizontal">
|
||||
|
||||
{products}
|
||||
|
||||
{provided.placeholder && (
|
||||
<div style={{display: 'none'}}>
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
</Droppable>
|
||||
);
|
||||
}
|
@ -16,7 +16,7 @@ export function Catalog() {
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const data = useShopStore((state) => state.groups);
|
||||
const items = useShopStore((state) => state.cards);
|
||||
const _items = useShopStore((state) => state.cards);
|
||||
|
||||
const onClickToggleMobileSideMenu = useShopStore((state) => state.switchSideMenu);
|
||||
const isMobile = useShopStore((state) => state.isMobile);
|
||||
|
@ -4,6 +4,7 @@ import {CrateMode} from "./CrateMode";
|
||||
import {CrateWarnings} from "./CrateWarnings";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {CrateOptions} from "./CrateOptions";
|
||||
import {CartHorizontal} from "./CartHorizontal";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
@ -28,7 +29,7 @@ export function Crate({crate_index}) {
|
||||
return (
|
||||
<div className="crate">
|
||||
{
|
||||
modes_order.includes(crate.crate_mode) ? (
|
||||
modes_order.includes(crate.crate_mode) && (
|
||||
<div className="crate-bar d-inline-flex justify-content-between">
|
||||
<CrateMode crate_index={crate_index}/>
|
||||
|
||||
@ -36,13 +37,17 @@ export function Crate({crate_index}) {
|
||||
Delete crate <img src="/images/shop/icon-remove.svg" alt="remove"/>
|
||||
</div>
|
||||
</div>
|
||||
) : <></>
|
||||
)
|
||||
}
|
||||
|
||||
<div className="crate-products">
|
||||
|
||||
<Cart crate_index={crate_index}/>
|
||||
|
||||
{ !modes_order.includes(crate.crate_mode) &&
|
||||
<CartHorizontal crate_index={crate_index}/>
|
||||
}
|
||||
|
||||
<CrateWarnings crate_index={crate_index} />
|
||||
|
||||
<CrateOptions crate_index={crate_index}/>
|
||||
|
@ -12,7 +12,7 @@ export function FakePlaceholder({isDraggingOver, nToDraw}) {
|
||||
<div key={i} style={{
|
||||
display: isDraggingOver ? 'none' : 'block',
|
||||
border: '1px dashed #ccc',
|
||||
width: '45px',
|
||||
width: '69px',
|
||||
marginBottom: '5px',
|
||||
}}></div>
|
||||
);
|
||||
|
@ -1,21 +1,23 @@
|
||||
import {DialogPopup} from "./options/DialogPopup";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {useShopStore, whichItems} from "./shop_store";
|
||||
import {SummaryPopup} from "./options/SummaryPopup";
|
||||
|
||||
export function OptionsDialogWrapper({crate_index, card_index, first, last}) {
|
||||
export function OptionsDialogWrapper({crate_index, card_index, horizontal}) {
|
||||
const whichH = whichItems(horizontal);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const options = useShopStore((state) => state.crates[crate_index].items[card_index].options);
|
||||
const options_data = useShopStore((state) => state.crates[crate_index].items[card_index].options_data);
|
||||
const card_size = useShopStore((state) => state.crates[crate_index].items[card_index].size);
|
||||
const card_id = useShopStore((state) => state.crates[crate_index].items[card_index].id);
|
||||
const options_class = useShopStore((state) => state.crates[crate_index].items[card_index].options_class);
|
||||
const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options);
|
||||
const options = useShopStore((state) => state.crates[crate_index][whichH][card_index][use_options]);
|
||||
const options_data = useShopStore((state) => state.crates[crate_index][whichH][card_index].options_data);
|
||||
const card_id = useShopStore((state) => state.crates[crate_index][whichH][card_index].id);
|
||||
const options_class = useShopStore((state) => state.crates[crate_index][whichH][card_index].options_class);
|
||||
const sideMenuIsOpen = useShopStore((state) => state.sideMenuIsOpen);
|
||||
const _notificationTimer = useShopStore((state) => state.notificationTimer);
|
||||
const hideNotification = useShopStore((state) => state.hideNotification);
|
||||
const displayNotification = useShopStore((state) =>
|
||||
const displayNotification = useShopStore((state) =>
|
||||
!!state.notificationHorizontal === !!horizontal &&
|
||||
state.notificationCrateId === crate_id &&
|
||||
(state.notificationCardIndex === card_index || (state.crates[crate_index].items.length + (state.notificationCardIndex || -1)) === card_index));
|
||||
(state.notificationCardIndex === card_index || (state.crates[crate_index][whichH].length + (state.notificationCardIndex || -1)) === card_index));
|
||||
|
||||
const onOptionsUpdate = useShopStore((state) => state.updateOptions);
|
||||
|
||||
@ -26,12 +28,10 @@ export function OptionsDialogWrapper({crate_index, card_index, first, last}) {
|
||||
options_class={options_class}
|
||||
key={"popover" + crate_id +card_id}
|
||||
id={"popover"+ crate_id + card_id}
|
||||
big={card_size === "big"}
|
||||
first={first}
|
||||
last={last}
|
||||
sideMenuIsOpen={sideMenuIsOpen}
|
||||
onHideNotification={hideNotification}
|
||||
displayNotification={displayNotification}
|
||||
horizontal={horizontal}
|
||||
target={{
|
||||
construct: ((outvar, value) => {
|
||||
// #!options_log
|
||||
@ -44,17 +44,19 @@ export function OptionsDialogWrapper({crate_index, card_index, first, last}) {
|
||||
console.log("update", outvar, value, options_data);
|
||||
|
||||
if (outvar in options_data) options_data[outvar] = value;
|
||||
onOptionsUpdate(crate_id, card_index, {[outvar]: value});
|
||||
onOptionsUpdate(crate_id, card_index, {[outvar]: value}, horizontal);
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function OptionsSummaryWrapper({crate_index, card_index}) {
|
||||
const card_id = useShopStore((state) => state.crates[crate_index].items[card_index].id);
|
||||
const options = useShopStore((state) => state.crates[crate_index].items[card_index].options);
|
||||
const options_data = useShopStore((state) => state.crates[crate_index].items[card_index].options_data);
|
||||
export function OptionsSummaryWrapper({crate_index, card_index, horizontal}) {
|
||||
const whichH = whichItems(horizontal);
|
||||
const card_id = useShopStore((state) => state.crates[crate_index][whichH][card_index].id);
|
||||
const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options);
|
||||
const options = useShopStore((state) => state.crates[crate_index][whichH][card_index][use_options]);
|
||||
const options_data = useShopStore((state) => state.crates[crate_index][whichH][card_index].options_data);
|
||||
|
||||
return (
|
||||
<SummaryPopup id={card_id + "options"} options={options}
|
||||
|
@ -8,7 +8,6 @@ import {RFQFeedback} from "./RFQFeedback";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
import {OrderOptions} from "./OrderOptions";
|
||||
|
||||
/**
|
||||
* Component that renders all things for order.
|
||||
|
@ -13,7 +13,7 @@ import {useRenderCount} from "@uidotdev/usehooks";
|
||||
* Component that renders a product.
|
||||
* Used in the crate
|
||||
*/
|
||||
export function ProductCartItem({card_index, crate_index, first, last}) {
|
||||
export function ProductCartItem({card_index, crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
@ -24,8 +24,12 @@ export function ProductCartItem({card_index, crate_index, first, last}) {
|
||||
const card_show_warnings = useShopStore(state => state.crates[crate_index].items[card_index].show_warnings, compareObjectsEmptiness);
|
||||
const card_counted_resources = useShopStore(state => state.crates[crate_index].items[card_index].counted_resources, compareObjectsEmptiness);
|
||||
|
||||
const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card);
|
||||
const options_disabled = useShopStore((state) => !!state.crateParams(state.crates[crate_index].crate_mode).warnings_disabled);
|
||||
const highlighted = useShopStore((state) =>
|
||||
!state.highlighted.horizontal &&
|
||||
state.crates[crate_index].id === state.highlighted.crate &&
|
||||
card_index === state.highlighted.card);
|
||||
const warnings_disabled = useShopStore((state) => !!state.crateParams(state.crates[crate_index].crate_mode).warnings_disabled);
|
||||
const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||
const removeHighlight = useShopStore((state) => state.highlightReset);
|
||||
@ -35,9 +39,9 @@ export function ProductCartItem({card_index, crate_index, first, last}) {
|
||||
console.log("ProductCartItem renders: ", renderCount)
|
||||
|
||||
|
||||
const options = !options_disabled && card && card.options && card.options.length > 0;
|
||||
const warnings = !options_disabled && card_show_warnings && card_show_warnings.length > 0;
|
||||
const resources = !options_disabled && card_counted_resources && card_counted_resources.length > 0;
|
||||
const options = use_options && card && card[use_options] && card[use_options].length > 0;
|
||||
const warnings = !warnings_disabled && card_show_warnings && card_show_warnings.length > 0;
|
||||
const resources = !warnings_disabled && card_counted_resources && card_counted_resources.length > 0;
|
||||
|
||||
return (
|
||||
<Draggable draggableId={card.id} index={card_index}>
|
||||
@ -57,7 +61,7 @@ export function ProductCartItem({card_index, crate_index, first, last}) {
|
||||
true
|
||||
)
|
||||
}}
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index)}
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index, false)}
|
||||
onMouseLeave={removeHighlight}
|
||||
>
|
||||
|
||||
@ -72,8 +76,6 @@ export function ProductCartItem({card_index, crate_index, first, last}) {
|
||||
<OptionsDialogWrapper
|
||||
crate_index={crate_index}
|
||||
card_index={card_index}
|
||||
first={first}
|
||||
last={last}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
96
static/js/shop/ProductCartItemHorizontal.jsx
Normal file
96
static/js/shop/ProductCartItemHorizontal.jsx
Normal file
@ -0,0 +1,96 @@
|
||||
import React from 'react'
|
||||
import {Draggable} from "@hello-pangea/dnd";
|
||||
import {productStyle} from "./utils";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {OptionsDialogWrapper} from "./OptionsWrapper";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
/**
|
||||
* Component that renders a product.
|
||||
* Used in the crate
|
||||
*/
|
||||
export function ProductCartItemHorizontal({card_index, crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const card = useShopStore((state) => state.crates[crate_index].h_items[card_index],
|
||||
(a, b) => a.id === b.id);
|
||||
|
||||
const highlighted = useShopStore((state) =>
|
||||
!!state.highlighted.horizontal &&
|
||||
state.crates[crate_index].id === state.highlighted.crate &&
|
||||
card_index === state.highlighted.card
|
||||
);
|
||||
const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||
const removeHighlight = useShopStore((state) => state.highlightReset);
|
||||
const onCardRemove = useShopStore((state) => state.deleteCard);
|
||||
|
||||
// #!render_count
|
||||
console.log("ProductCartItem renders: ", renderCount)
|
||||
|
||||
|
||||
const options = use_options && card && card[use_options] && card[use_options].length > 0;
|
||||
|
||||
return (
|
||||
<Draggable draggableId={card.id} index={card_index}>
|
||||
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={{
|
||||
...productStyle(
|
||||
provided.draggableProps.style,
|
||||
snapshot,
|
||||
true,
|
||||
!!highlighted,
|
||||
false,
|
||||
true
|
||||
)
|
||||
}}
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index, true)}
|
||||
onMouseLeave={removeHighlight}
|
||||
>
|
||||
|
||||
{/* warning container */}
|
||||
|
||||
<div className="progress-container warning d-flex justify-content-evenly">
|
||||
{options && (
|
||||
<OptionsDialogWrapper
|
||||
crate_index={crate_index}
|
||||
card_index={card_index}
|
||||
horizontal={true}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="product-name"
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index, true)}
|
||||
onClick={() => setHighlight(crate_id, card_index, true)}
|
||||
>
|
||||
|
||||
{`${card.name_number} ${card.name} ${card.name_codename}`}
|
||||
</div>
|
||||
|
||||
{/* remove container */}
|
||||
<div
|
||||
style={{'display': 'flex'}}
|
||||
className="removeHorizontal"
|
||||
onClick={() => onCardRemove(crate_id, card_index, true)}>
|
||||
|
||||
<img src="/images/shop/icon-remove.svg" alt="rm"/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
</Draggable>
|
||||
);
|
||||
}
|
@ -7,6 +7,8 @@ import {useShopStore} from "./shop_store";
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
|
||||
const DNDIcon = "/images/shop/icon-drag-and-drop.svg";
|
||||
|
||||
function DatasheetLink({datasheet_file, datasheet_name}) {
|
||||
return datasheet_file && datasheet_name && (<div className="ds">
|
||||
<span className='doc-icon'></span>
|
||||
@ -76,11 +78,12 @@ export function ProductItem({card_index}) {
|
||||
snapshot,
|
||||
true, // hack: remove weird animation after a drop
|
||||
)}
|
||||
src={card.image}/>
|
||||
className={card.image ? "" : "default-icon"}
|
||||
src={card.image || DNDIcon}/>
|
||||
|
||||
{/* Allows to simulate a clone */}
|
||||
{snapshot.isDragging && (
|
||||
<img className="simclone" src={card.image}/>
|
||||
<img className={"simclone " + (card.image ? "" : "default-icon")} src={card.image || DNDIcon}/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
@ -15,6 +15,7 @@ export function SummaryCrate({crate_index}) {
|
||||
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const crate_len = useShopStore((state) => state.crates[crate_index].items.length);
|
||||
const crate_h_len = useShopStore((state) => state.crates[crate_index].h_items.length);
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrate renders: ", renderCount)
|
||||
@ -28,6 +29,14 @@ export function SummaryCrate({crate_index}) {
|
||||
<SummaryCrateCard crate_index={crate_index} card_index={index} key={"summary_crate_" + crate_id + "_" +index} />
|
||||
)}
|
||||
|
||||
{range(0, crate_h_len).map((index, _i) =>
|
||||
<SummaryCrateCard
|
||||
crate_index={crate_index}
|
||||
card_index={index}
|
||||
horizontal={true}
|
||||
key={"summary_crate_h_" + crate_id + "_" +index} />
|
||||
)}
|
||||
|
||||
<SummaryCratePricedOptions crate_index={crate_index}/>
|
||||
</tbody>
|
||||
)
|
||||
|
@ -1,45 +1,56 @@
|
||||
import {compareObjectsEmptiness, formatMoney} from "./utils";
|
||||
import {WarningIndicator} from "./CardWarnings";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {useShopStore, whichItems} from "./shop_store";
|
||||
import {OptionsSummaryWrapper} from "./OptionsWrapper";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
|
||||
export function SummaryCrateCard({crate_index, card_index}) {
|
||||
export function SummaryCrateCard({crate_index, card_index, horizontal}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const whichH = whichItems(horizontal);
|
||||
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const deleteCard = useShopStore((state) => state.deleteCard);
|
||||
const setHighlight = useShopStore((state) => state.highlightCard);
|
||||
const resetHighlight = useShopStore((state) => state.highlightReset);
|
||||
|
||||
const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card);
|
||||
const highlighted = useShopStore((state) =>
|
||||
state.highlighted.horizontal === horizontal &&
|
||||
state.crates[crate_index].id === state.highlighted.crate &&
|
||||
card_index === state.highlighted.card);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const card = useShopStore((state) => state.crates[crate_index].items[card_index],
|
||||
const card = useShopStore((state) =>
|
||||
state.crates[crate_index][whichH][card_index],
|
||||
(a, b) => a.id === b.id);
|
||||
const card_show_warnings = useShopStore(state => state.crates[crate_index].items[card_index].show_warnings, compareObjectsEmptiness);
|
||||
const card_options_data = useShopStore(state => state.crates[crate_index].items[card_index].options_data, compareObjectsEmptiness);
|
||||
const options_disabled = useShopStore((state) => !!state.crateParams(state.crates[crate_index].crate_mode).warnings_disabled);
|
||||
|
||||
// additional hooks for updating warning and options
|
||||
const card_show_warnings = useShopStore(state =>
|
||||
state.crates[crate_index][whichH][card_index].show_warnings,
|
||||
compareObjectsEmptiness);
|
||||
const card_options_data = useShopStore(state =>
|
||||
state.crates[crate_index][whichH][card_index].options_data,
|
||||
compareObjectsEmptiness);
|
||||
const warnings_disabled = useShopStore((state) => !!state.crateParams(state.crates[crate_index].crate_mode).warnings_disabled);
|
||||
const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options);
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrateCard renders: ", renderCount)
|
||||
|
||||
|
||||
const options = !options_disabled && card && card.options && card.options.length > 0;
|
||||
const options_data = !options_disabled && card_options_data && Object.keys(card_options_data).length > 0;
|
||||
const warnings = !options_disabled && card_show_warnings && card_show_warnings.length > 0;
|
||||
const options = use_options && card && card[use_options] && card[use_options].length > 0;
|
||||
const options_data = card_options_data && Object.keys(card_options_data).length > 0;
|
||||
const warnings = !warnings_disabled && card_show_warnings && card_show_warnings.length > 0;
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={"summary_crate_" + crate_id + "_" + card_index}
|
||||
className={`hoverable ${highlighted ? 'selected' : ''}`}
|
||||
onClick={() => setHighlight(crate_id, card_index)}
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index)}
|
||||
onClick={() => setHighlight(crate_id, card_index, horizontal)}
|
||||
onMouseEnter={() => setHighlight(crate_id, card_index, horizontal)}
|
||||
onMouseLeave={() => resetHighlight()}>
|
||||
<td className="item-card-name tabbed">
|
||||
<div>{`${card.name_number} ${card.name} ${card.name_codename}`}</div>
|
||||
@ -49,7 +60,7 @@ export function SummaryCrateCard({crate_index, card_index}) {
|
||||
<div className="d-inline-flex align-content-center">
|
||||
{`${currency} ${formatMoney(card.price)}`}
|
||||
|
||||
<button onClick={() => deleteCard(crate_id, card_index)}>
|
||||
<button onClick={() => deleteCard(crate_id, card_index, horizontal)}>
|
||||
<img src="/images/shop/icon-remove.svg" className="d-block"/>
|
||||
</button>
|
||||
|
||||
@ -64,7 +75,10 @@ export function SummaryCrateCard({crate_index, card_index}) {
|
||||
}}> </span>
|
||||
))}
|
||||
{((options && options_data) ? (
|
||||
<OptionsSummaryWrapper crate_index={crate_index} card_index={card_index}/>
|
||||
<OptionsSummaryWrapper
|
||||
crate_index={crate_index}
|
||||
card_index={card_index}
|
||||
horizontal={horizontal}/>
|
||||
) : (
|
||||
<span style={{
|
||||
'display': 'inline-block',
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {formatMoney} from "./utils";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {ProcessOptions, ProcessOptionsToData} from "./options/Options";
|
||||
import {ProcessOptions} from "./options/Options";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
@ -27,7 +27,7 @@ const count_item_occupied_hp = (item) => {
|
||||
}
|
||||
|
||||
const count_item_occupied_tec = (item) => {
|
||||
return (item.options_data && item.options_data.tec === false) ? 0 : ((item.consumes && item.consumes.tec) || 0);
|
||||
return (item.options_data && item.options_data.tec === false) ? 0 : (item.consumes && item.consumes.tec);
|
||||
}
|
||||
|
||||
export const item_occupied_counters = {
|
||||
|
@ -21,10 +21,13 @@ export function validateJSON(description) {
|
||||
|
||||
try {
|
||||
for (const crate of crates_raw) {
|
||||
if (!crate.type || !crate.items || !crate.options || !(crate.type in crate_modes)) return false;
|
||||
if (!crate.type || !crate.items || !crate.h_items || !crate.options || !(crate.type in crate_modes)) return false;
|
||||
for (const card of crate.items) {
|
||||
if (!(card.pn in pn_to_card) || card.options === undefined) return false;
|
||||
}
|
||||
for (const card of crate.h_items) {
|
||||
if (!(card.pn in pn_to_card) || card.options === undefined) return false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
@ -50,6 +53,11 @@ export function JSONToCrates(description) {
|
||||
id: uuidv4(),
|
||||
options_data: card.options || {}
|
||||
}))),
|
||||
h_items: Array.from(crate.h_items.map((card, _i) => ({
|
||||
...pn_to_card(card.pn),
|
||||
id: uuidv4(),
|
||||
options_data: card.options || {}
|
||||
}))),
|
||||
warnings: [],
|
||||
occupiedHP: 0,
|
||||
})));
|
||||
@ -65,12 +73,17 @@ export function CratesToJSON(crates) {
|
||||
const crateOptions = useShopStore.getState().crate_options;
|
||||
const orderOptions = useShopStore.getState().order_options;
|
||||
const orderOptionsData = useShopStore.getState().order_options_data;
|
||||
const crateParams = useShopStore.getState().crateParams;
|
||||
return JSON.stringify({
|
||||
// additional fields can go here
|
||||
crates: Array.from(crates.map((crate, _i) => ({
|
||||
items: Array.from(crate.items.map((card, _) => ({
|
||||
pn: card.name_number,
|
||||
options: (card.options_data && card.options) ? FilterOptions(card.options, card.options_data) : null
|
||||
options: (card.options_data && card[crateParams(crate.crate_mode).options]) ? FilterOptions(card[crateParams(crate.crate_mode).options], card.options_data) : null
|
||||
}))),
|
||||
h_items: Array.from(crate.h_items.map((card, _) => ({
|
||||
pn: card.name_number,
|
||||
options: (card.options_data && card[crateParams(crate.crate_mode).options]) ? FilterOptions(card[crateParams(crate.crate_mode).options], card.options_data) : null
|
||||
}))),
|
||||
type: crate.crate_mode,
|
||||
options: FilterOptions(crateOptions, crate.options_data)
|
||||
|
@ -1,46 +1,56 @@
|
||||
import React, {useState} from "react";
|
||||
import {useClickAway} from "./useClickAway";
|
||||
import React, {useRef, useState} from "react";
|
||||
import {ProcessOptions} from "./Options";
|
||||
import {Notification} from "./Notification";
|
||||
import {Overlay} from "react-bootstrap";
|
||||
|
||||
export function DialogPopup({options, data, target, id, big, first, last, options_class,
|
||||
sideMenuIsOpen, displayNotification, onHideNotification}) {
|
||||
export function DialogPopup({options, data, target, id, options_class,
|
||||
sideMenuIsOpen, displayNotification, onHideNotification, horizontal}) {
|
||||
const [show, setShow] = useState(false);
|
||||
const ref = useClickAway((e) => {
|
||||
if (e.type === "mousedown") // ignore touchstart
|
||||
setShow(false)
|
||||
}
|
||||
);
|
||||
const ref = useRef(null);
|
||||
|
||||
let div_classes = `overlayVariant border rounded ${options_class || ""}`;
|
||||
|
||||
let div_classes = `overlayVariant border rounded ${big ? "overlay-bigcard" : "overlay-smallcard"} ${(!big && first) ? "overlay-first" : ""} ${(!big && last && !first) ? "overlay-last" : ""} ${options_class || ""}`;
|
||||
const handleClick = (_event) => {
|
||||
setShow(!show);
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
<Notification
|
||||
id={"processed_options_notification" + id}
|
||||
tip="Customization options available"
|
||||
sideMenuIsOpen={sideMenuIsOpen}
|
||||
show={displayNotification}
|
||||
onHide={onHideNotification}
|
||||
content={
|
||||
<img className="alert-info"
|
||||
src={show ? "/images/shop/icon-close.svg" : "/images/shop/icon-customize.svg"}
|
||||
onClick={handleClick}/>
|
||||
}
|
||||
/>
|
||||
<div style={{'display': show ? 'flex' : 'none'}} className={div_classes}>
|
||||
<ProcessOptions
|
||||
options={options}
|
||||
data={data}
|
||||
key={"processed_options_" + id}
|
||||
id={"processed_options_" + id}
|
||||
target={target}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (<>
|
||||
<Notification
|
||||
id={"processed_options_notification" + id}
|
||||
tip="Customization options available"
|
||||
sideMenuIsOpen={sideMenuIsOpen}
|
||||
show={displayNotification}
|
||||
onHide={onHideNotification}
|
||||
content={
|
||||
<img className="alert-info" ref={ref}
|
||||
src={show ? "/images/shop/icon-close.svg" : "/images/shop/icon-customize.svg"}
|
||||
onClick={handleClick}/>
|
||||
}
|
||||
/>
|
||||
<Overlay target={ref.current}
|
||||
show={show}
|
||||
placement={horizontal ? "right" : "bottom"}
|
||||
onHide={() => setShow(false)}
|
||||
rootClose={true}>
|
||||
{({
|
||||
placement: _placement,
|
||||
arrowProps: _arrowProps,
|
||||
show: _show,
|
||||
popper: _popper,
|
||||
hasDoneInitialMeasure: _hasDoneInitialMeasure,
|
||||
...props
|
||||
}) => (
|
||||
<div style={{'display': show ? 'flex' : 'none', ...props.style}} {...props} className={div_classes}>
|
||||
<ProcessOptions
|
||||
options={options}
|
||||
data={data}
|
||||
key={"processed_options_" + id}
|
||||
id={"processed_options_" + id}
|
||||
target={target}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</>);
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
|
||||
|
||||
const ipv4 = (params) => {
|
||||
const ipv4 = (_params) => {
|
||||
const ipv4WithMaskPattern = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(0|1[0-9]|2[0-9]|3[0-2]|[0-9])$/;
|
||||
return (text) => {
|
||||
return ipv4WithMaskPattern.test(text);
|
||||
}
|
||||
}
|
||||
|
||||
const ipv6 = (params) => {
|
||||
const ipv6 = (_params) => {
|
||||
const ipv6WithMaskPattern = /(^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(\/(\d{1,2}|1[0-1]\d|12[0-8]))(%.+)?\s*$)/;
|
||||
|
||||
|
||||
@ -16,6 +16,35 @@ const ipv6 = (params) => {
|
||||
}
|
||||
}
|
||||
|
||||
const hostname = (_params) => {
|
||||
const maxHostnameLength = 253;
|
||||
const maxLabelLength = 63;
|
||||
const labelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
|
||||
|
||||
return (text) => {
|
||||
if (text.length > maxHostnameLength) {
|
||||
return false;
|
||||
}
|
||||
const labels = text.split('.');
|
||||
for (const label of labels) {
|
||||
if (label.length < 1
|
||||
|| label.length > maxLabelLength
|
||||
|| !labelRegex.test(label)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const ipv4OrHost = (params) => {
|
||||
const hostnameLocal = hostname(params);
|
||||
const ipv4Local = ipv4(params);
|
||||
return (text) => {
|
||||
return ipv4Local(text) || hostnameLocal(text);
|
||||
}
|
||||
}
|
||||
|
||||
const ipv4or6 = (params) => {
|
||||
const ipv4Local = ipv4(params);
|
||||
const ipv6Local = ipv6(params);
|
||||
@ -47,5 +76,7 @@ export const Validation = {
|
||||
ipv4: ipv4,
|
||||
ipv6: ipv6,
|
||||
ipv4or6: ipv4or6,
|
||||
hostname: hostname,
|
||||
ipv4OrHost: ipv4OrHost,
|
||||
frequency: frequency
|
||||
};
|
@ -2,7 +2,7 @@
|
||||
|
||||
import {createWithEqualityFn} from "zustand/traditional";
|
||||
import {DATA as shared_data, itemsUnfoldedList, API_RFQ} from "./utils";
|
||||
import {FillExtCrateData, FillExtOrderData, true_type_of} from "./options/utils";
|
||||
import {FillExtCrateData, FillExtOrderData} from "./options/utils";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import {FillResources} from "./count_resources";
|
||||
import {FillExtCardData} from "./options/utils";
|
||||
@ -13,12 +13,24 @@ import {ProcessOptionsToData} from "./options/Options";
|
||||
import {DomainedRFQMessages} from "./Domained";
|
||||
|
||||
|
||||
export const HORIZONTAL_CART_MARKER = "_h";
|
||||
|
||||
const cards_to_pn_map = (cards) => {
|
||||
let result = {};
|
||||
Object.entries(cards).forEach(([key, card], _i) => { result[card.name_number] = key})
|
||||
return result;
|
||||
};
|
||||
|
||||
const toArray = (arg) => {
|
||||
return Array.isArray(arg) ? arg : [arg];
|
||||
};
|
||||
|
||||
const unwrapCrateId = (crate_id= "") => {
|
||||
return crate_id.endsWith(HORIZONTAL_CART_MARKER) ? [true, crate_id.substring(0, crate_id.length - HORIZONTAL_CART_MARKER.length)] : [false, crate_id]
|
||||
}
|
||||
|
||||
export const whichItems = (horizontal = false) => horizontal ? "h_items" : "items"
|
||||
|
||||
const useCatalog = ((set, get) => ({
|
||||
cards: shared_data.items,
|
||||
groups: shared_data.columns.catalog,
|
||||
@ -33,21 +45,24 @@ const useCatalog = ((set, get) => ({
|
||||
const useOptionsNotification = ((set, get) => ({
|
||||
notificationCrateId: null,
|
||||
notificationCardIndex: null,
|
||||
notificationHorizontal: false,
|
||||
notificationTimer: null,
|
||||
_showNotification: (crate_id, card_index) => set(state => ({
|
||||
_showNotification: (crate_id, card_index, horizontal) => set(state => ({
|
||||
notificationCrateId: crate_id,
|
||||
notificationCardIndex: card_index,
|
||||
notificationHorizontal: horizontal,
|
||||
notificationTimer: setTimeout(() => {
|
||||
state.hideNotification()
|
||||
}, 5000)
|
||||
})),
|
||||
showNotification: (crate_id, card_index) => {
|
||||
showNotification: (crate_id, card_index, horizontal) => {
|
||||
get().hideNotification()
|
||||
setTimeout(() => get()._showNotification(crate_id, card_index), 100);
|
||||
setTimeout(() => get()._showNotification(crate_id, card_index, horizontal), 100);
|
||||
},
|
||||
hideNotification: () => set(state => ({
|
||||
notificationCrateId: null,
|
||||
notificationCardIndex: null,
|
||||
notificationHorizontal: false,
|
||||
notificationTimer: (state.notificationTimer && clearTimeout(state.notificationTimer)) || null,
|
||||
}))
|
||||
}));
|
||||
@ -211,7 +226,7 @@ const useImportJSON = ((set, get) => ({
|
||||
get().fillExtCrateData(crate.id);
|
||||
});
|
||||
get()._updateTotalOrderPrice();
|
||||
get().showNotification(get().active_crate, null);
|
||||
get().showNotification(get().active_crate, null, false);
|
||||
},
|
||||
updateImportDescription: (new_description) => set(state => ({
|
||||
importValue: {
|
||||
@ -328,15 +343,17 @@ const useSubmitForm = ((set, get) => ({
|
||||
const useHighlighted = ((set, get) => ({
|
||||
highlighted: {
|
||||
crate: "",
|
||||
card: 0
|
||||
card: 0,
|
||||
horizontal: false
|
||||
},
|
||||
highlightedTimer: null,
|
||||
|
||||
// #!if disable_card_highlight === false
|
||||
highlightCard: (crate_id, index) => set(state => ({
|
||||
highlightCard: (crate_id, index, horizontal) => set(state => ({
|
||||
highlighted: {
|
||||
crate: crate_id,
|
||||
card: index
|
||||
card: index,
|
||||
horizontal: horizontal
|
||||
},
|
||||
highlightedTimer: (!!state.highlightedTimer ? clearTimeout(state.highlightedTimer) : null) || (state.isTouch && setTimeout(() => {
|
||||
get().highlightReset()
|
||||
@ -345,7 +362,8 @@ const useHighlighted = ((set, get) => ({
|
||||
highlightReset: () => set(state => ({
|
||||
highlighted: {
|
||||
crate: "",
|
||||
card: 0
|
||||
card: 0,
|
||||
horizontal: false
|
||||
},
|
||||
highlightedTimer: !!state.highlightedTimer ? clearTimeout(state.highlightedTimer) : null
|
||||
})),
|
||||
@ -384,17 +402,18 @@ const useCart = ((set, get) => ({
|
||||
})
|
||||
})),
|
||||
setActiveCrate: (id) => set(state => ({active_crate: id})),
|
||||
_addCardFromCatalog: (crate_to, index_from, index_to) => set(state => {
|
||||
const take_from = (true_type_of(index_from) === "array" ? index_from : [index_from]).map((item, _i) => (state.cards_list[item]));
|
||||
_addCardFromCatalog: (crate_to, index_from, index_to, horizontal) => set(state => {
|
||||
const whichH = whichItems(horizontal)
|
||||
const take_from = toArray(index_from).map((item, _i) => (state.cards_list[item]));
|
||||
const dest = crate_to || state.active_crate;
|
||||
if (!dest) return {};
|
||||
return {
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (dest === crate.id) {
|
||||
index_to = index_to != null ? index_to : crate.items.length;
|
||||
index_to = index_to != null ? index_to : crate[whichH].length;
|
||||
return {
|
||||
...crate,
|
||||
items: crate.items.toSpliced(index_to, 0, ...take_from.map((card_name, _) => {
|
||||
[whichH]: crate[whichH].toSpliced(index_to, 0, ...take_from.map((card_name, _) => {
|
||||
return {...state.cards[card_name], id: uuidv4()}
|
||||
}))
|
||||
}
|
||||
@ -402,39 +421,41 @@ const useCart = ((set, get) => ({
|
||||
})
|
||||
}
|
||||
}),
|
||||
_moveCard: (crate_from, index_from, crate_to, index_to) => set(state => {
|
||||
const the_card = state.crates.find((crate, _) => crate_from === crate.id ).items[index_from];
|
||||
_moveCard: (crate_from, index_from, crate_to, index_to, horizontal) => set(state => {
|
||||
const whichH = whichItems(horizontal)
|
||||
const the_card = state.crates.find((crate, _) => crate_from === crate.id )[whichH][index_from];
|
||||
return {
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_to === crate_from && crate_to === crate.id) {
|
||||
let items_copy = Array.from(crate.items);
|
||||
let items_copy = Array.from(crate[whichH]);
|
||||
let item = items_copy.splice(index_from, 1)[0]
|
||||
items_copy.splice(index_to, 0, item).filter((item, _) => !!item)
|
||||
return {
|
||||
...crate,
|
||||
items: items_copy
|
||||
[whichH]: items_copy
|
||||
}
|
||||
} else if (crate_to === crate.id) {
|
||||
return {
|
||||
...crate,
|
||||
items: crate.items.toSpliced(index_to, 0, the_card)
|
||||
[whichH]: crate[whichH].toSpliced(index_to, 0, the_card)
|
||||
}
|
||||
} else if (crate_from === crate.id) {
|
||||
return {
|
||||
...crate,
|
||||
items: crate.items.toSpliced(index_to, 1)
|
||||
[whichH]: crate[whichH].toSpliced(index_to, 1)
|
||||
}
|
||||
}
|
||||
else return crate;
|
||||
})
|
||||
}
|
||||
}),
|
||||
_deleteCard: (crate_id, index) => set(state => ({
|
||||
_deleteCard: (crate_id, index, horizontal) => set(state => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_id === crate.id) {
|
||||
const whichH = whichItems(horizontal)
|
||||
return {
|
||||
...crate,
|
||||
items: crate.items.toSpliced(index, 1)
|
||||
[whichH]: crate[whichH].toSpliced(index, 1)
|
||||
}
|
||||
}
|
||||
else return crate;
|
||||
@ -445,7 +466,8 @@ const useCart = ((set, get) => ({
|
||||
if (id === crate.id) {
|
||||
return {
|
||||
...crate,
|
||||
items: []
|
||||
items: [],
|
||||
h_items: []
|
||||
}
|
||||
}
|
||||
else return crate;
|
||||
@ -454,10 +476,11 @@ const useCart = ((set, get) => ({
|
||||
clearAll: () => set(state => ({
|
||||
crates: state._defaultCrates
|
||||
})),
|
||||
_updateOptions: (crate_id, index, new_options) => set(state => ({
|
||||
_updateOptions: (crate_id, index, new_options, horizontal) => set(state => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_id === crate.id) {
|
||||
let itemsCopy = Array.from(crate.items);
|
||||
const whichH = whichItems(horizontal)
|
||||
let itemsCopy = Array.from(crate[whichH]);
|
||||
itemsCopy[index] = {
|
||||
...itemsCopy[index],
|
||||
options_data: {
|
||||
@ -466,7 +489,7 @@ const useCart = ((set, get) => ({
|
||||
}};
|
||||
return {
|
||||
...crate,
|
||||
items: itemsCopy
|
||||
[whichH]: itemsCopy
|
||||
}
|
||||
}
|
||||
else return crate;
|
||||
@ -476,7 +499,7 @@ const useCart = ((set, get) => ({
|
||||
fillWarnings: (crate_id) => set(state => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_id === crate.id) {
|
||||
//console.log("--- CHECK ALERTS ---")
|
||||
// Warnings for horizontal items are not available
|
||||
let itemsCopy = Array.from(crate.items);
|
||||
const disabled = !!get().crateParams(crate.crate_mode).warnings_disabled;
|
||||
itemsCopy = FillResources(itemsCopy, disabled);
|
||||
@ -493,20 +516,23 @@ const useCart = ((set, get) => ({
|
||||
})
|
||||
})),
|
||||
|
||||
fillExtData: (crate_id) => set(state => ({
|
||||
fillExtData: (crate_id, horizontal) => set(state => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
// horizontal items do not interact with each other for now
|
||||
if (crate_id === crate.id) {
|
||||
let itemsCopy = Array.from(crate.items);
|
||||
const whichH = whichItems(horizontal)
|
||||
const options_name = state.crateParams(crate.crate_mode).options;
|
||||
let itemsCopy = Array.from(crate[whichH]);
|
||||
|
||||
itemsCopy = itemsCopy.map((item, index) => {
|
||||
if (!item.options) return item;
|
||||
if (!item[options_name]) return item;
|
||||
if (!item.options_data) item.options_data = {};
|
||||
item.options_data.ext_data = FillExtCardData(itemsCopy, index);
|
||||
return item;
|
||||
});
|
||||
return {
|
||||
...crate,
|
||||
items: Array.from(itemsCopy)
|
||||
[whichH]: Array.from(itemsCopy)
|
||||
}
|
||||
}
|
||||
else return crate;
|
||||
@ -519,7 +545,7 @@ const useCart = ((set, get) => ({
|
||||
sum += get().crate_modes[crate.crate_mode].price;
|
||||
const crate_options = ProcessOptionsToData({options: get().crate_prices, data: crate.options_data || {}});
|
||||
sum += crate_options ? crate_options.reduce((accumulator, currentValue) => accumulator+currentValue.price, 0) : 0;
|
||||
crate.items.forEach((item, _) => {
|
||||
crate.items.concat(crate.h_items).forEach((item, _) => {
|
||||
sum += item.price;
|
||||
});
|
||||
});
|
||||
@ -534,6 +560,7 @@ const useCart = ((set, get) => ({
|
||||
const crate_id = "crate" + get().crates.length;
|
||||
get()._newCrate(crate_id)
|
||||
get().fillExtData(crate_id);
|
||||
get().fillExtData(crate_id, true);
|
||||
get().fillExtCrateData(crate_id);
|
||||
get().fillOrderExtData();
|
||||
get().fillWarnings(crate_id);
|
||||
@ -543,6 +570,7 @@ const useCart = ((set, get) => ({
|
||||
setCrateMode: (id, mode) => {
|
||||
get()._setCrateMode(id, mode)
|
||||
get().fillExtData(id);
|
||||
get().fillExtData(id, true);
|
||||
get().fillExtCrateData(id);
|
||||
get().fillOrderExtData();
|
||||
get().fillWarnings(id);
|
||||
@ -557,15 +585,18 @@ const useCart = ((set, get) => ({
|
||||
},
|
||||
|
||||
addCardFromCatalog: (crate_to, index_from, index_to, just_mounted) => {
|
||||
const dest = crate_to || get().active_crate;
|
||||
const isCrateless = toArray(index_from).some(value => get().getCardDescription(value).crateless === true);
|
||||
const [isHorizontal, crateTo] = crate_to ? unwrapCrateId(crate_to) : [false, crate_to];
|
||||
const isHorizontalOnly = toArray(index_from).some(value => !!get().getCardDescription(value).horizontal);
|
||||
const dest = isCrateless ? "spare" : crateTo || get().active_crate;
|
||||
if (!dest) {
|
||||
console.warn("No destination");
|
||||
get().noDestinationWarning();
|
||||
return {};
|
||||
}
|
||||
get().showNotification(dest, index_to);
|
||||
get()._addCardFromCatalog(dest, index_from, index_to)
|
||||
get().fillExtData(dest);
|
||||
get().showNotification(dest, index_to, isHorizontalOnly);
|
||||
get()._addCardFromCatalog(dest, index_from, index_to, isHorizontalOnly)
|
||||
get().fillExtData(dest, isHorizontalOnly);
|
||||
get().fillWarnings(dest);
|
||||
get().setActiveCrate(dest);
|
||||
get()._updateTotalOrderPrice();
|
||||
@ -575,22 +606,25 @@ const useCart = ((set, get) => ({
|
||||
},
|
||||
|
||||
moveCard: (crate_from, index_from, crate_to, index_to) => {
|
||||
get()._moveCard(crate_from, index_from, crate_to, index_to);
|
||||
get().fillExtData(crate_to);
|
||||
get().fillWarnings(crate_to);
|
||||
get().setActiveCrate(crate_to);
|
||||
const [isHorizontal, crateFrom] = unwrapCrateId(crate_from)
|
||||
const [_, crateTo] = unwrapCrateId(crate_to)
|
||||
get()._moveCard(crateFrom, index_from, crateTo, index_to, isHorizontal);
|
||||
get().fillExtData(crateTo, isHorizontal);
|
||||
get().fillWarnings(crateTo);
|
||||
get().setActiveCrate(crateTo);
|
||||
get()._updateTotalOrderPrice();
|
||||
if (crate_from !== crate_to) {
|
||||
get().fillExtData(crate_from);
|
||||
get().fillWarnings(crate_from);
|
||||
if (crateFrom !== crate_to) {
|
||||
get().fillExtData(crateFrom, isHorizontal);
|
||||
get().fillWarnings(crateFrom);
|
||||
}
|
||||
},
|
||||
deleteCard: (crate_id, index) => {
|
||||
get()._deleteCard(crate_id, index);
|
||||
get().fillExtData(crate_id);
|
||||
get().fillWarnings(crate_id);
|
||||
deleteCard: (crate_id, index, horizontal) => {
|
||||
const [isHorizontal, crateId] = horizontal ? [horizontal, crate_id] : unwrapCrateId(crate_id);
|
||||
get()._deleteCard(crateId, index, isHorizontal);
|
||||
get().fillExtData(crateId, isHorizontal);
|
||||
get().fillWarnings(crateId);
|
||||
get()._updateTotalOrderPrice();
|
||||
if (crate_id === get().highlighted.crate && index === get().highlighted.card) get().highlightReset()
|
||||
if (crateId === get().highlighted.crate && index === get().highlighted.card) get().highlightReset()
|
||||
},
|
||||
clearCrate: (id) => {
|
||||
get()._clearCrate(id);
|
||||
@ -598,16 +632,19 @@ const useCart = ((set, get) => ({
|
||||
get()._updateTotalOrderPrice();
|
||||
},
|
||||
|
||||
updateOptions: (crate_id, index, new_options) => {
|
||||
get()._updateOptions(crate_id, index, new_options);
|
||||
get().fillExtData(crate_id);
|
||||
get().fillWarnings(crate_id);
|
||||
updateOptions: (crate_id, index, new_options, horizontal) => {
|
||||
get()._updateOptions(crate_id, index, new_options, horizontal);
|
||||
get().fillExtData(crate_id, horizontal);
|
||||
if (!horizontal) {
|
||||
get().fillWarnings(crate_id);
|
||||
}
|
||||
},
|
||||
|
||||
initExtData: () => {
|
||||
get().fillOrderExtData();
|
||||
get().crates.forEach((crate, _i) => {
|
||||
get().fillExtData(crate.id);
|
||||
get().fillExtData(crate.id, true);
|
||||
get().fillExtData(crate.id, false);
|
||||
get().fillExtCrateData(crate.id);
|
||||
})
|
||||
get()._updateTotalOrderPrice();
|
||||
|
@ -6,20 +6,23 @@ const shop_data = {
|
||||
id: 'rack',
|
||||
name: 'Rack mountable crate',
|
||||
price: 550,
|
||||
hp: 84
|
||||
hp: 84,
|
||||
options: "options"
|
||||
},
|
||||
desktop: {
|
||||
id: 'desktop',
|
||||
name: 'Desktop crate',
|
||||
price: 500,
|
||||
hp: 42
|
||||
hp: 42,
|
||||
options: "options"
|
||||
},
|
||||
no_crate: {
|
||||
id: 'no_crate',
|
||||
name: 'Spare cards',
|
||||
name: 'Spare items',
|
||||
price: 0,
|
||||
hp: -1,
|
||||
warnings_disabled: true
|
||||
warnings_disabled: true,
|
||||
options: "crateless_options"
|
||||
}
|
||||
},
|
||||
crateModeOrder: [
|
||||
@ -129,12 +132,6 @@ const shop_data = {
|
||||
{type: "Line", args: {title: "Please provide delivery address",
|
||||
outvar: "shipping_instructions", fallback: "",}}],
|
||||
]},
|
||||
{type: "Switch", args: {
|
||||
title: "I would like this order made outside Hong Kong (see FAQ)",
|
||||
outvar: "outside_hk",
|
||||
tip: "For the convenience of our customers who may be discouraged from doing business with companies registered in Hong Kong, M-Labs can manufacture Sinara hardware orders upon request in Manila, Philippines. Additional fees and terms may apply.",
|
||||
fallback: false,
|
||||
}},
|
||||
]}
|
||||
],
|
||||
|
||||
@ -184,8 +181,6 @@ const shop_data = {
|
||||
'4 MMCX clock outputs.',
|
||||
'Price includes bitstream generation, flashing, testing, and firmware updates for 1 year (USD 1,400.00).',
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/1124.pdf',
|
||||
datasheet_name: '1124 Carrier Kasli 2.0 datasheet',
|
||||
size: 'big',
|
||||
options: [
|
||||
{type: "Radio", args: {title: "DRTIO role", outvar: "drtio_role", variants: ["standalone", "master", "satellite"], tip: "Distributed Real Time Input/Output allows ARTIQ RTIO channels to be distributed among several satellite devices synchronized and controlled by a central core(master) device. Standalone option disables this feature."}},
|
||||
@ -250,8 +245,6 @@ const shop_data = {
|
||||
'12 EEM connectors.',
|
||||
'4 MMCX clock outputs.',
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/1125.pdf',
|
||||
datasheet_name: '1124 Carrier Kasli SoC datasheet',
|
||||
size: 'big',
|
||||
hp: 8,
|
||||
nbrSlotMin: 0,
|
||||
@ -476,7 +469,7 @@ const shop_data = {
|
||||
name: 'MCX-TTL',
|
||||
name_number: '2238',
|
||||
name_codename: '',
|
||||
price: 700,
|
||||
price: 600,
|
||||
image: '/images/shop/graphic-03_MCX-TTL.svg',
|
||||
specs: [
|
||||
'16 single-ended digital signals on MCX connectors.',
|
||||
@ -870,8 +863,6 @@ const shop_data = {
|
||||
'Each card provides 8 channels.',
|
||||
'Breaking out all 32 channels from a Zotino requires 4 IDC-BNC cards.'
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/5518-5528.pdf',
|
||||
datasheet_name: '5518 BNC-IDC / 5528 SMA-IDC datasheet',
|
||||
size: 'big',
|
||||
warnings: [
|
||||
"no_idc_source"
|
||||
@ -886,17 +877,26 @@ const shop_data = {
|
||||
name: 'HV Amplifier',
|
||||
name_number: '5633',
|
||||
name_codename: '',
|
||||
price: 1950,
|
||||
price: 1500,
|
||||
image: '/images/shop/graphic-03_HVAMP32.svg',
|
||||
specs: [
|
||||
'Amplifier: LTC6090, 32 channels.',
|
||||
'Output voltage: ±50V.',
|
||||
'Amplifier: LTC6090 or OPA462, 32 channels.',
|
||||
'Amplification range up to ±25V and ±55V available.',
|
||||
'Over-temperature protection.',
|
||||
'External 12V power supply.',
|
||||
'Slew rate 21V/us typical.',
|
||||
'Bandwidth: 12MHz/gain, 2.4MHz typical.',
|
||||
'Bandwidth: 12MHz/gain, for ±25V range BW is 4.8MHz typical.',
|
||||
'The same connector as in Zotino and Fastino.'
|
||||
],
|
||||
options: [
|
||||
{type: "Radio",
|
||||
args: {
|
||||
title: "Amplification range",
|
||||
outvar: "hvamp",
|
||||
variants: ["±25V", "±55V"],
|
||||
fallback: 0
|
||||
}},
|
||||
],
|
||||
size: 'small',
|
||||
warnings: [
|
||||
"no_idc_source"
|
||||
@ -921,8 +921,6 @@ const shop_data = {
|
||||
'Each card provides 8 channels.',
|
||||
'Breaking out all 32 channels from a Zotino requires 4 SMA-IDC cards.'
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/5518-5528.pdf',
|
||||
datasheet_name: '5518 BNC-IDC / 5528 SMA-IDC datasheet',
|
||||
size: 'small',
|
||||
warnings: [
|
||||
"no_idc_source"
|
||||
@ -937,7 +935,7 @@ const shop_data = {
|
||||
name: 'MCX-IDC',
|
||||
name_number: '5538',
|
||||
name_codename: '',
|
||||
price: 900,
|
||||
price: 320,
|
||||
image: '/images/shop/graphic-03_MCX-IDC32.svg',
|
||||
specs: [
|
||||
'Breaks out analog signals from Zotino or HD68-IDC to MCX connectors.',
|
||||
@ -963,8 +961,6 @@ const shop_data = {
|
||||
specs: [
|
||||
'Connects an external HD68 cable to IDC-BNC, IDC-SMA or IDC-MCX cards.',
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/5568.pdf',
|
||||
datasheet_name: '5568 HD68-IDC datasheet',
|
||||
size: 'small',
|
||||
options: [
|
||||
{type: "Radio", args: {title: "Cable length", outvar: "hd68_cable_len", variants: ["1 M", "2 M", "3 M"], tip: "The desired length of the HD68 cable", fallback: 1}},
|
||||
@ -996,8 +992,6 @@ const shop_data = {
|
||||
'Full-scale input ranges between +-10mV and +-10V.',
|
||||
'Supports SU-Servo laser intensity stabilization servo in conjunction with Urukul.'
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/5108.pdf',
|
||||
datasheet_name: '5108 ADC Sampler datasheet',
|
||||
options: [
|
||||
{type: "Switch", args: {title: "1 EEM mode", outvar: "mono_eem"}},
|
||||
{
|
||||
@ -1072,8 +1066,6 @@ const shop_data = {
|
||||
'Frequency up to 1GHz.',
|
||||
'Low jitter <100fs RMS.'
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/7210.pdf',
|
||||
datasheet_name: '7210 Clocker datasheet',
|
||||
options: [
|
||||
{type: "Switch", args: {title: "Ext CLK", outvar: "ext_clk"}},
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}}
|
||||
@ -1114,10 +1106,24 @@ const shop_data = {
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}},
|
||||
{type: "Switch", args: {title: "Term #0", outvar: "term_0", tip: "Enable termination on ADC channel #0"}},
|
||||
{type: "Switch", args: {title: "Term #1", outvar: "term_1", tip: "Enable termination on ADC channel #1"}}
|
||||
],
|
||||
crateless_options: [
|
||||
{type: "SwitchLine", args: {title: "IP", outvar: "ip",
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
],
|
||||
options_class: "stabilizer",
|
||||
size: 'small',
|
||||
warnings: [
|
||||
@ -1148,10 +1154,24 @@ const shop_data = {
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}},
|
||||
{type: "Switch", args: {title: "Term #0", outvar: "term_0", tip: "Enable termination on ADC channel #0"}},
|
||||
{type: "Switch", args: {title: "Term #1", outvar: "term_1", tip: "Enable termination on ADC channel #1"}}
|
||||
],
|
||||
crateless_options: [
|
||||
{type: "SwitchLine", args: {title: "IP", outvar: "ip",
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
],
|
||||
options_class: "stabilizer",
|
||||
size: 'small',
|
||||
warnings: [
|
||||
@ -1177,8 +1197,6 @@ const shop_data = {
|
||||
'Lower jitter and phase noise.',
|
||||
'Large frequency changes take several milliseconds.',
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/4456.pdf',
|
||||
datasheet_name: '4456 Synthesizer Mirny datasheet',
|
||||
options: [
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 600e6}},
|
||||
@ -1239,7 +1257,25 @@ const shop_data = {
|
||||
'100Base-T Ethernet with PoE.'
|
||||
],
|
||||
options: [
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}}
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}},
|
||||
{type: "SwitchLine", args: {title: "Static IPv4", outvar: "ip",
|
||||
validator: {name: "ipv4"},
|
||||
fallback: {text: "0.0.0.0", checked: false},
|
||||
tip: "Set up static IPv4 address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
],
|
||||
crateless_options: [
|
||||
{type: "SwitchLine", args: {title: "Static IPv4", outvar: "ip",
|
||||
validator: {name: "ipv4"},
|
||||
fallback: {text: "0.0.0.0", checked: false},
|
||||
tip: "Set up static IPv4 address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
],
|
||||
size: 'small',
|
||||
warnings: [
|
||||
@ -1264,6 +1300,12 @@ const shop_data = {
|
||||
'100Base-T Ethernet with PoE.',
|
||||
'Can stabilize temperature of Sinara 5432 DAC or external devices containing TEC and thermistor.'
|
||||
],
|
||||
crateless_options: [
|
||||
{type: "SwitchLine", args: {title: "IPv4", outvar: "ip",
|
||||
validator: {name: "ipv4"},
|
||||
fallback: {text: "192.168.1.26/24", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
],
|
||||
size: 'small',
|
||||
consumes: {
|
||||
hp: 4
|
||||
@ -1315,12 +1357,26 @@ const shop_data = {
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
fallback: {text: "125 MHz", checked: false}, validator: {name: "frequency", params: {min: 10e6, max: 1e9}}}},
|
||||
{type: "Switch", args: {title: "Termination #0", outvar: "term_0", tip: "Enable termination on ADC channel #0"}},
|
||||
{type: "Switch", args: {title: "Termination #1", outvar: "term_1", tip: "Enable termination on ADC channel #1"}}
|
||||
],
|
||||
crateless_options: [
|
||||
{type: "SwitchLine", args: {title: "Static IPv4", outvar: "ip",
|
||||
validator: {name: "ipv4"},
|
||||
fallback: {text: "0.0.0.0", checked: false},
|
||||
tip: "Set up static IPv4 address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "MQTT broker address", outvar: "broker",
|
||||
validator: {name: "ipv4OrHost"},
|
||||
fallback: {text: "mqtt", checked: false},
|
||||
tip: "Set up domain name or IPv4 of the MQTT broker"}},
|
||||
],
|
||||
size: 'big',
|
||||
warnings: [
|
||||
"no_eem_source",
|
||||
@ -1387,6 +1443,27 @@ const shop_data = {
|
||||
hp: 8,
|
||||
},
|
||||
},
|
||||
'afws': {
|
||||
id: 'afws',
|
||||
name: 'Subscription',
|
||||
name_number: 'AFWS',
|
||||
name_codename: '',
|
||||
price: 800,
|
||||
image: null,
|
||||
horizontal: true,
|
||||
specs: [
|
||||
"Artiq Firmware Service for one variant for one year.",
|
||||
"Includes support at helpdesk.",
|
||||
"Included with purchase of any Carrier with no additional cost.",
|
||||
],
|
||||
crateless: true,
|
||||
crateless_options: [
|
||||
{type: "Line", args: {title: "Variant name", outvar: "variant_name", fallback: "",
|
||||
tip: "Variant name can be found on the sticker on top of the crate. If you don't have one, leave the preferred name here."}},
|
||||
],
|
||||
size: 'big',
|
||||
warnings: [],
|
||||
},
|
||||
},
|
||||
|
||||
columns: {
|
||||
@ -1444,6 +1521,7 @@ const shop_data = {
|
||||
'koster',
|
||||
'eem_pwr_mod',
|
||||
'kirdy',
|
||||
'afws',
|
||||
]}
|
||||
],
|
||||
},
|
||||
@ -1453,14 +1531,16 @@ const shop_data = {
|
||||
crate_mode: "rack",
|
||||
fan_tray: false,
|
||||
items: [],
|
||||
h_items: [],
|
||||
warnings: [],
|
||||
occupiedHP: 0,
|
||||
},
|
||||
{
|
||||
id: "spare",
|
||||
name: "Spare cards",
|
||||
name: "Spare items",
|
||||
crate_mode: "no_crate",
|
||||
items: [],
|
||||
h_items: [],
|
||||
warnings: [],
|
||||
occupiedHP: 0,
|
||||
}
|
||||
|
40
static/sw.js
Normal file
40
static/sw.js
Normal file
@ -0,0 +1,40 @@
|
||||
var CACHE_NAME = 'ml-03';
|
||||
var urlsToCache = [
|
||||
'/css/bootstrap-5.3.0.min.css',
|
||||
'/css/styles.css',
|
||||
'/js/jquery-3.7.0.slim.min.js',
|
||||
'/js/jquery-3.7.0.min.js',
|
||||
'/js/bootstrap-5.3.0.min.js',
|
||||
'/js/react.production.min.js',
|
||||
'/js/react-dom.production.min.js',
|
||||
'/js/prop-types.min.js',
|
||||
'/js/react-beautiful-dnd.min.js',
|
||||
'/js/uuid_v4@latest.js',
|
||||
];
|
||||
|
||||
self.addEventListener('install', function(event) {
|
||||
console.log('[SW] Install');
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(function(cache) {
|
||||
console.log('[SW] add to cache');
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
console.log('[SW] Fetch');
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(function (response) {
|
||||
return response || fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('message', function (event) {
|
||||
if (event.data.action === 'skipWaiting') {
|
||||
self.skipWaiting();
|
||||
caches.delete(CACHE_NAME);
|
||||
}
|
||||
});
|
@ -43,7 +43,7 @@
|
||||
<link href="{{ get_url(path='favicon.ico', cachebust=true) }}" rel="shortcut icon" type="image/x-icon">
|
||||
<link href="{{ get_url(path='favicon.ico', cachebust=true) }}" rel="icon" type="image/x-icon">
|
||||
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/bootstrap-5.3.0.opt.css', cachebust=true) }}">
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/bootstrap-5.3.0.min.css', cachebust=true) }}">
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/styles.css', cachebust=true) }}">
|
||||
{% block styles %}{% endblock %}
|
||||
</head>
|
||||
@ -85,7 +85,7 @@
|
||||
</a>
|
||||
<div class="dropdown-menu shadow-none shadow-lg text-end text-lg-start" aria-labelledby="navbarDropdown">
|
||||
{% for page in subsection.pages %}
|
||||
<a class="dropdown-item pt-2 pb-2 pt-sm-3 pb-sm-3" href="{{ page.permalink | safe }}">{{ page.title }}</a>
|
||||
<a class="dropdown-item pt-2 pb-2 pt-sm-3 pb-sm-3" href="{{ page.permalink }}">{{ page.title }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
@ -93,7 +93,7 @@
|
||||
{% elif subsection.title != 'Other' %}
|
||||
|
||||
<li class="nav-item ms-0 ms-md-4">
|
||||
<a class="nav-link" href="{{ subsection.permalink | safe }}">{{ subsection.title }}</a>
|
||||
<a class="nav-link" href="{{ subsection.permalink }}">{{ subsection.title }}</a>
|
||||
</li>
|
||||
|
||||
{% endif %}
|
||||
@ -155,6 +155,7 @@
|
||||
|
||||
<!-- ./FOOTER -->
|
||||
|
||||
<script src="{{ get_url(path='js/jquery-3.7.0.slim.min.js', cachebust=true) }}"></script>
|
||||
<script src="{{ get_url(path='js/bootstrap-5.3.0.bundle.min.js', cachebust=true) }}"></script>
|
||||
|
||||
<script>
|
||||
|
@ -17,8 +17,8 @@
|
||||
<meta property="og:description" content="{% block og_description%}{{ config.description }}{% endblock %}">
|
||||
{% endblock %}
|
||||
<meta property="og:site_name" content="{{ config.extra.author }}">
|
||||
<meta property="og:url" content="{{ config.base_url | safe }}">
|
||||
<meta property="og:image" content="{{ get_url(path='images/logo@2x.png', cachebust=true) }}">
|
||||
<meta property="og:url" content="{{ config.base_url }}">
|
||||
<meta property="og:image" content={{ get_url(path='images/logo@2x.png', cachebust=true) }}>
|
||||
{% block meta %}{% endblock meta %}
|
||||
|
||||
<meta name="theme-color" content="#715ec7">
|
||||
@ -42,21 +42,11 @@
|
||||
<link href="{{ get_url(path='favicon.ico', cachebust=true) }}" rel="shortcut icon" type="image/x-icon">
|
||||
<link href="{{ get_url(path='favicon.ico', cachebust=true) }}" rel="icon" type="image/x-icon">
|
||||
|
||||
<link href="https://m-labs.hk{{ current_path | safe }}" rel="canonical" >
|
||||
<link href="https://m-labs.hk{{ current_path | safe }}" rel="alternate" hreflang="x-default" >
|
||||
<link href="https://m-labs.ph{{ current_path | safe }}" rel="alternate" hreflang="en-ph" >
|
||||
<link href="https://m-labs-intl.com{{ current_path | safe }}" rel="alternate" hreflang="en-us" >
|
||||
|
||||
{% block links %}{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/bootstrap-5.3.0.opt.css', cachebust=true) }}">
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/styles.css', cachebust=true) }}">
|
||||
{% endblock %}
|
||||
{% block styles %}{% endblock %}
|
||||
|
||||
</head>
|
||||
|
||||
<body itemscope itemtype="https://schema.org/WebPage">
|
||||
<body>
|
||||
|
||||
<!-- HEADER -->
|
||||
|
||||
@ -64,7 +54,7 @@
|
||||
|
||||
<header class="container">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-th-1 justify-content-center justify-content-between pt-md-3 pb-md-3" role="navigation" itemscope itemtype="http://schema.org/SiteNavigationElement">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-th-1 justify-content-center justify-content-between pt-md-3 pb-md-3" role="navigation">
|
||||
|
||||
<a class="navbar-brand logo me-0" href="/">
|
||||
<img src="{{ get_url(path='images/logo@2x.png') }}" height="40" alt="logo">
|
||||
@ -94,11 +84,11 @@
|
||||
<div class="dropdown-menu shadow-none shadow-lg text-start text-lg-start" aria-labelledby="navbarDropdown">
|
||||
{% for tmp_page in subsection.pages %}
|
||||
{% if tmp_page.extra.menu_item and tmp_page.extra.menu_item == "th1" %}
|
||||
<a class="dropdown-item pt-2 pb-2 mx-4 mt-2 mb-2 mt-sm-3 mb-sm-3 w-auto btn btn-primary btn-inversed {% if current_path == tmp_page.path %}active{% endif %} rounded-1" href="{{ tmp_page.permalink | safe }}" itemprop="url">
|
||||
<a class="dropdown-item pt-2 pb-2 mx-4 mt-2 mb-2 mt-sm-3 mb-sm-3 w-auto btn btn-primary btn-inversed {% if current_path == tmp_page.path %}active{% endif %} rounded-1" href="{{ tmp_page.permalink }}">
|
||||
<center>{{ tmp_page.title }}</center>
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="dropdown-item pt-2 pb-2 {% if current_path == tmp_page.path %}active{% endif %}" href="{{ tmp_page.permalink | safe }}" itemprop="url">{{ tmp_page.title }}</a>
|
||||
<a class="dropdown-item pt-2 pb-2 {% if current_path == tmp_page.path %}active{% endif %}" href="{{ tmp_page.permalink }}">{{ tmp_page.title }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
@ -107,7 +97,7 @@
|
||||
{% elif subsection.title != 'Other' %}
|
||||
|
||||
<li class="nav-item ms-0 ms-md-4 {% if current_path == subsection.path %}active{% endif %}">
|
||||
<a class="nav-link" href="{{ subsection.permalink | safe }}">{{ subsection.title }}</a>
|
||||
<a class="nav-link" href="{{ subsection.permalink }}">{{ subsection.title }}</a>
|
||||
</li>
|
||||
|
||||
{% endif %}
|
||||
@ -136,6 +126,25 @@
|
||||
{% endblock %}
|
||||
<!-- ./MAIN CONTENT -->
|
||||
|
||||
<noscript id="deferred-main-styles">
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/bootstrap-5.3.0.min.css', cachebust=true) }}">
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/styles.css', cachebust=true) }}">
|
||||
{% block deferred_styles %}{% endblock %}
|
||||
</noscript>
|
||||
<script>
|
||||
var loadDeferredMainStyles = function() {
|
||||
var addStylesNode = document.getElementById("deferred-main-styles");
|
||||
var replacement = document.createElement("div");
|
||||
replacement.innerHTML = addStylesNode.textContent;
|
||||
document.body.appendChild(replacement)
|
||||
addStylesNode.parentElement.removeChild(addStylesNode);
|
||||
};
|
||||
var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
if (raf) raf(function() { window.setTimeout(loadDeferredMainStyles, 0); });
|
||||
else window.addEventListener('load', loadDeferredMainStyles);
|
||||
</script>
|
||||
|
||||
<!-- FOOTER -->
|
||||
|
||||
{% block footer %}
|
||||
@ -147,8 +156,8 @@
|
||||
|
||||
{% endblock %}
|
||||
|
||||
<p class="pt-4 pb-2 text-center" itemprop="copyrightNotice">
|
||||
<small>Copyright © <span id="copyright_year" itemprop="copyrightYear">{{ now() | date(format="%Y") }}</span>, <span itemprop="copyrightHolder">M-Labs</span>. All rights reserved. ARTIQ, the ARTIQ logo, Migen and nMigen are registered or unregistered trademarks.</small>
|
||||
<p class="pt-4 pb-2 text-center">
|
||||
<small>Copyright © <span id="copyright_year"></span>, M-Labs. All rights reserved. ARTIQ, the ARTIQ logo, Migen and nMigen are registered or unregistered trademarks.</small>
|
||||
</p>
|
||||
|
||||
{% block fcopyright %}
|
||||
@ -173,7 +182,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ get_url(path='js/bootstrap-5.3.0.bundle.min.js', cachebust=true) }}" defer></script>
|
||||
|
||||
<!-- <script src="{{ get_url(path='js/as.js', cachebust=true) }}"></script> -->
|
||||
|
||||
<script src="{{ get_url(path='js/jquery-3.7.0.min.js', cachebust=true) }}"></script>
|
||||
<script src="{{ get_url(path='js/bootstrap-5.3.0.bundle.min.js', cachebust=true) }}"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@ -187,20 +200,16 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const modal = document.getElementById('modalEnlarged');
|
||||
const enlargedImg = document.getElementById('enlarged');
|
||||
|
||||
modal.addEventListener('hidden.bs.modal', function() {
|
||||
enlargedImg.src = '';
|
||||
$(function () {
|
||||
$('#modalEnlarged').on('hidden.bs.modal', function (e) {
|
||||
$('#enlarged').attr('src', '');
|
||||
});
|
||||
document.querySelectorAll('[data-popup]').forEach(function(element) {
|
||||
element.addEventListener('click', function() {
|
||||
const imgSrc = this.dataset.nsrc;
|
||||
enlargedImg.src = imgSrc;
|
||||
const bsModal = new bootstrap.Modal(modal);
|
||||
bsModal.show();
|
||||
});
|
||||
|
||||
$('[data-popup]').click(function (evt) {
|
||||
var _self = $(this);
|
||||
var _img_enlarged = _self.data('nsrc');
|
||||
$('#enlarged').attr('src', _img_enlarged);
|
||||
$('#modalEnlarged').modal('show');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@ -1,32 +1,28 @@
|
||||
<div class="row pt-5 pb-5">
|
||||
|
||||
<div class="col-12 col-md-6 pb-5 pb-md-0" itemscope itemtype="https://schema.org/ContactPoint">
|
||||
<div class="col-12 col-md-6 pb-5 pb-md-0">
|
||||
|
||||
<h5 class="pb-3" itemprop="contactType">Sales inquiries</h5>
|
||||
<h5 class="pb-3">Sales inquiries</h5>
|
||||
|
||||
<p itemprop="description">
|
||||
To purchase ARTIQ Sinara hardware, software and gateware development services, or to enter a technical support agreement, write to <a href="mailto:sales@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}" itemprop="email">sales@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}</a>.
|
||||
<p>
|
||||
To purchase ARTIQ Sinara hardware, software and gateware development services, or to enter a technical support agreement, write to <a href="mailto:sales@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}">sales@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}</a>.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 pb-5 pb-md-0">
|
||||
|
||||
<div class="pb-3" itemscope itemtype="https://schema.org/ContactPoint">
|
||||
<h5 class="pb-3" itemprop="contactType">Commercial technical support</h5>
|
||||
<h5 class="pb-3">Commercial technical support</h5>
|
||||
|
||||
<p>
|
||||
If you need help setting up ARTIQ hardware purchased from us, or if you have a commercial technical support agreement with us, email us anytime at <a href="mailto:helpdesk@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}" itemprop="email">helpdesk@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}</a>.
|
||||
If you need help setting up ARTIQ hardware purchased from us, or if you have a commercial technical support agreement with us, email us anytime at <a href="mailto:helpdesk@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}">helpdesk@{{get_env(name="DOMAINNAME", default="m-labs.hk")}}</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-3" itemscope itemtype="https://schema.org/ContactPoint">
|
||||
<h5 class="pb-3" itemprop="contactType">Community support</h5>
|
||||
<h5 class="pb-3">Community support</h5>
|
||||
|
||||
<p itemprop="description">
|
||||
Our open source projects such as ARTIQ have an active user community. Get and offer help on the <a href="https://chat.m-labs.hk/" target="_blank" rel="noopener noreferrer" itemprop="url">Mattermost chat</a>, on IRC #m-labs on OFTC, or on <a href="https://forum.m-labs.hk/" rel="noopener noreferrer" target="_blank" itemprop="url">the forum</a>.
|
||||
<p>
|
||||
Our open source projects such as ARTIQ have an active user community. Get and offer help on the <a href="https://chat.m-labs.hk/" target="_blank" rel="noopener noreferrer">Mattermost chat</a>, on IRC #m-labs on OFTC, or on <a href="https://forum.m-labs.hk/" rel="noopener noreferrer" target="_blank">the forum</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
{% block links %}
|
||||
<link rel="preload" href="images/hero@2x.png" as="image" >
|
||||
{% endblock %}
|
||||
|
||||
{% block hero %}
|
||||
|
||||
@ -14,7 +11,7 @@
|
||||
|
||||
<div class="col-12 text-center">
|
||||
|
||||
<h1 itemprop="headline">Open tools for open physics.</h1>
|
||||
<h1>Open tools for open physics.</h1>
|
||||
|
||||
<img src="{{ get_url(path='images/ion@2x.png', cachebust=true) }}" height="12" alt="ion">
|
||||
|
||||
@ -37,15 +34,14 @@
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<div class="card shadow mt-3 mb-3" itemscope itemtype="https://schema.org/Poster">
|
||||
<div class="card shadow mt-3 mb-3">
|
||||
|
||||
<div class="card-body p-3 p-md-5 card-artiq">
|
||||
|
||||
<div class="col-12 col-md-6 ps-0 pe-0">
|
||||
|
||||
<h5 class="card-title" itemprop="headline">ARTIQ</h5>
|
||||
<h5 class="card-title">ARTIQ</h5>
|
||||
|
||||
<div class="desc-wrapper" itemprop="description">
|
||||
<p class="card-text pt-3">
|
||||
ARTIQ (Advanced Real-Time Infrastructure for Quantum physics) is a leading-edge control system for quantum information experiments, developed in partnership with a growing number of research institutions worldwide.
|
||||
</p>
|
||||
@ -53,13 +49,12 @@
|
||||
<p class="card-text pt-3 pb-3">
|
||||
The system features a high-level programming language that helps describe complex experiments, which is compiled and executed on dedicated hardware with nanosecond timing resolution and sub-microsecond latency.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="{{ get_url(path='@/experiment-control/place-order.md') }}" class="btn btn-primary btn-inversed btn-lg" itemprop="url">Order hardware</a>
|
||||
<a href="https://m-labs.hk/artiq/manual/" class="btn btn-primary btn-lg" itemprop="url">Manual</a>
|
||||
<a href="https://forum.m-labs.hk" class="btn btn-primary btn-lg" itemprop="url">Forum</a>
|
||||
<a href="{{ get_url(path='@/experiment-control/artiq.md') }}" class="btn btn-primary btn-lg" itemprop="url">More...</a>
|
||||
<div class="btn-group download-selector" >
|
||||
<a href="{{ get_url(path='@/experiment-control/place-order.md') }}" class="btn btn-primary btn-inversed btn-lg">Order hardware</a>
|
||||
<a href="https://m-labs.hk/artiq/manual/" class="btn btn-primary btn-lg">Manual</a>
|
||||
<a href="https://forum.m-labs.hk" class="btn btn-primary btn-lg">Forum</a>
|
||||
<a href="{{ get_url(path='@/experiment-control/artiq.md') }}" class="btn btn-primary btn-lg">More...</a>
|
||||
<div class="btn-group download-selector">
|
||||
<a class="btn btn-primary btn-lg d-inline-flex" href="https://nixbld.m-labs.hk/job/artiq/extra/msys2-offline-installer/latest/download/1">
|
||||
<img src="/images/icons/icon-windows.svg" class="d-inline-block align-self-center mx-1" style="height: 1rem" alt="windows">
|
||||
<span class="d-inline-block">Download <sup>.exe</sup></span>
|
||||
@ -69,25 +64,15 @@
|
||||
<span>stable</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu shadow dropdown-menu-end">
|
||||
<li itemscope itemtype="https://schema.org/SoftwareApplication">
|
||||
<a class="dropdown-item" href="https://nixbld.m-labs.hk/job/artiq/extra/msys2-offline-installer/latest/download/1" itemprop="downloadUrl">
|
||||
<span itemprop="softwareVersion"><span itemprop="name">ARTIQ</span>-8 (stable) for <span itemprop="operatingSystem">Windows</span></span>
|
||||
<li>
|
||||
<a class="dropdown-item" href="https://nixbld.m-labs.hk/job/artiq/extra/msys2-offline-installer/latest/download/1">
|
||||
ARTIQ-8 (stable) for Windows
|
||||
</a>
|
||||
<div hidden itemprop="offers" itemtype="https://schema.org/Offer" itemscope>
|
||||
<div itemprop="price">
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li itemscope itemtype="https://schema.org/SoftwareApplication">
|
||||
<a class="dropdown-item" href="https://nixbld.m-labs.hk/job/artiq/extra-beta/msys2-offline-installer/latest/download/1" itemprop="downloadUrl">
|
||||
<span itemprop="softwareVersion"><span itemprop="name">ARTIQ</span>-9 (beta) for <span itemprop="operatingSystem">Windows</span></span>
|
||||
<li>
|
||||
<a class="dropdown-item" href="https://nixbld.m-labs.hk/job/artiq/extra-beta/msys2-offline-installer/latest/download/1">
|
||||
ARTIQ-9 (beta) for Windows
|
||||
</a>
|
||||
<div hidden itemprop="offers" itemtype="https://schema.org/Offer" itemscope>
|
||||
<div itemprop="price">
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -105,24 +90,22 @@
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<div class="card shadow mt-3 mb-3" itemscope itemtype="https://schema.org/Poster">
|
||||
<div class="card shadow mt-3 mb-3">
|
||||
|
||||
<div class="card-body p-3 p-md-5 card-jobs">
|
||||
|
||||
<div class="col-12 col-md-6 ps-0 pe-0">
|
||||
|
||||
<h5 class="card-title" itemprop="headline">Jobs</h5>
|
||||
<h5 class="card-title">Jobs</h5>
|
||||
|
||||
<div class="desc-wrapper" itemprop="description">
|
||||
<p class="card-text pt-3">
|
||||
We are always looking for talented people to join our team and help us deliver the systems that enable the experimental quantum research of tomorrow.
|
||||
</p>
|
||||
<p class="card-text pt-3">
|
||||
If you are good at software, electronics or photonics, and physics makes you tick, then you should get in touch. Locations in Hong Kong and Manila available.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="{{ get_url(path='@/about-us/jobs.md') }}" class="btn btn-primary btn-lg" itemprop="url">Find out more...</a>
|
||||
<a href="{{ get_url(path='@/about-us/jobs.md') }}" class="btn btn-primary btn-lg">Find out more...</a>
|
||||
|
||||
</div>
|
||||
|
||||
@ -138,15 +121,14 @@
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<div class="card shadow mt-3 mb-3" itemscope itemtype="https://schema.org/Poster">
|
||||
<div class="card shadow mt-3 mb-3">
|
||||
|
||||
<div class="card-body p-3 p-md-5 card-gateware">
|
||||
|
||||
<div class="col-12 col-md-6 ps-0 pe-0">
|
||||
|
||||
<h5 class="card-title" itemprop="headline">Gateware and system-on-chip design</h5>
|
||||
<h5 class="card-title">Gateware and system-on-chip design</h5>
|
||||
|
||||
<div class="desc-wrapper" itemprop="description">
|
||||
<p class="card-text pt-3">
|
||||
Traditional gateware design with Verilog and VHDL is well known to be tedious and inefficient. M-Labs have developed Migen, a Python-based HDL and toolbox that addresses many of their issues and makes gateware design more productive.
|
||||
</p>
|
||||
@ -154,9 +136,8 @@
|
||||
<p class="card-text pt-3 pb-3">
|
||||
Built on Migen, MiSoC provides a high performance, flexible and lightweight solution to build system-on-chips for various applications.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="{{ get_url(path='@/gateware/migen.md') }}" class="btn btn-primary btn-lg" itemprop="url">Read more...</a>
|
||||
<a href="{{ get_url(path='@/gateware/migen.md') }}" class="btn btn-primary btn-lg">Read more...</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
{% extends 'section.html' %}
|
||||
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/bootstrap-5.3.0.min.css', cachebust=true) }}">
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/styles.css', cachebust=true) }}">
|
||||
{% block deferred_styles %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/order-hardware.css', cachebust=true) }}">
|
||||
{% endblock %}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends 'section.html' %}
|
||||
|
||||
{% block styles %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
h5 {
|
||||
margin-bottom: 2rem!important;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{% block main %}
|
||||
|
||||
<main class="container {% block maincss %}{% endblock %}" xmlns="http://www.w3.org/1999/html">
|
||||
<main class="container {% block maincss %}{% endblock %}">
|
||||
|
||||
<div class="row">
|
||||
{% block main_content %}
|
||||
@ -14,28 +14,26 @@
|
||||
{% set publications = load_data(path=page.extra.data, format="yaml") -%}
|
||||
{% for publication in publications %}
|
||||
<div class="col">
|
||||
<div class="card shadow h-100" itemscope itemtype="https://schema.org/ScholarlyArticle">
|
||||
<div class="card shadow h-100" >
|
||||
<div class="card-body">
|
||||
<strong class="card-title" itemprop="headline">{{ publication.title | safe }}</strong>
|
||||
<strong class="card-title">{{ publication.title | safe }}</strong>
|
||||
{% if publication.authors.short or publication.authors.full %}
|
||||
<p class="card-text">
|
||||
<span itemprop="author">
|
||||
{{ publication.authors.short | safe }}
|
||||
<span class="collapse" id="viewdetails{{ publication.title | slugify | truncate(length=20, end="")}}">
|
||||
{{ publication.authors.full }}
|
||||
</span>
|
||||
</span>
|
||||
<a data-bs-toggle="collapse" class="hide-expanded" data-bs-target="#viewdetails{{ publication.title | slugify | truncate(length=20, end="") }}"> ...more</a>
|
||||
<a data-bs-toggle="collapse" class="show-expanded" data-bs-target="#viewdetails{{ publication.title | slugify | truncate(length=20, end="") }}"><br>less</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="card-text" itemprop="author">{{ publication.authors }}</p>
|
||||
<p class="card-text">{{ publication.authors }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-footer border-light-subtle">
|
||||
{% for link in publication.links %}
|
||||
{% if link.path %}
|
||||
<a href="{{link.path | safe}}" class="card-link" itemprop="archivedAt">{{link.name}}</a>
|
||||
<a href="{{link.path}}" class="card-link">{{link.name}}</a>
|
||||
{% else %}
|
||||
<div class="card-link d-inline">{{link.name}}</div>
|
||||
{% endif %}
|
||||
|
@ -1,23 +1,23 @@
|
||||
<div class="{% if css %}{{ css }}{% else %}col-12 col-md-4{% endif %} mb-4">
|
||||
|
||||
<div class="{% if not rmcard %}card shadow{% endif %} h-100" itemscope itemtype="https://schema.org/Thing">
|
||||
<div class="{% if not rmcard %}card shadow{% endif %} h-100">
|
||||
|
||||
{% if not imgbottom %}
|
||||
|
||||
{% if src %}
|
||||
<div class="px-auto pt-4">
|
||||
<img src="{{ get_url(path=src, cachebust=true) }}" width="40px" itemprop="image">
|
||||
<img src="{{ get_url(path=src, cachebust=true) }}" width="40px">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-body">
|
||||
{% if title %}
|
||||
<div class="card-title" itemprop="name">
|
||||
<div class="card-title">
|
||||
<h5 class="mb-0">{{ title }}</h5>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div {% if sameheight %}style="min-height: {{ sameheight }}px"{% endif%} itemprop="description">
|
||||
<div {% if sameheight %}style="min-height: {{ sameheight }}px"{% endif%}>
|
||||
{{ body | markdown | safe }}
|
||||
</div>
|
||||
|
||||
@ -27,18 +27,18 @@
|
||||
|
||||
<div class="card-body">
|
||||
{% if title %}
|
||||
<div class="card-title" itemprop="name">
|
||||
<div class="card-title">
|
||||
<h5 class="mb-0">{{ title }}</h5>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<span itemprop="description">{{ body | markdown | safe }}</span>
|
||||
{{ body | markdown | safe }}
|
||||
|
||||
</div>
|
||||
|
||||
{% if src %}
|
||||
<div class="">
|
||||
<img src="{{ get_url(path=src, cachebust=true) }}" class="card-img img-fluid" itemprop="image">
|
||||
<img src="{{ get_url(path=src, cachebust=true) }}" class="card-img img-fluid">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -1,24 +1,24 @@
|
||||
{% if position == "left" %}
|
||||
|
||||
<div class="card mb-3 shadow" style="max-width: 80%;margin-right: 20%;" itemscope itemtype="https://schema.org/Grant">
|
||||
<div class="card mb-3 shadow" style="max-width: 80%;margin-right: 20%;">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4 funding-logo-start d-flex p-1 justify-content-center">
|
||||
{% if logo %}
|
||||
{% if logo_link %}<a href="{{ logo_link | safe }}" class="align-self-center" itemprop="url">{% endif %}
|
||||
<img src="{{ logo | safe }}" class="img-fluid rounded" alt="{{ logo_alt }}" itemprop="image"/>
|
||||
{% if logo_link %}<a href="{{ logo_link }}" class="align-self-center">{% endif %}
|
||||
<img src="{{ logo }}" class="img-fluid rounded" alt="{{ logo_alt }}"/>
|
||||
{% if logo_link %}</a>{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
{% if title %}<h5 class="card-title" itemprop="sponsor">{{ title | markdown | safe }}</h5>{% endif %}
|
||||
{% if title %}<h5 class="card-title">{{ title | markdown | safe }}</h5>{% endif %}
|
||||
{% if icon %}
|
||||
<span class="badge bg-transparent position-absolute top-0 start-100 translate-middle-x">
|
||||
<img src="{{ icon | safe }}" alt="{{ icon }}" class="rounded"
|
||||
<img src="{{ icon }}" alt="{{ icon }}" class="rounded"
|
||||
style="height: 2rem;"/>
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="card-text" itemprop="description">{{ body | markdown | safe }}</div>
|
||||
<p class="card-text">{{ body | markdown | safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,24 +26,24 @@
|
||||
|
||||
{% elif position == "right" %}
|
||||
|
||||
<div class="card mb-3 shadow" style="max-width: 80%;margin-left: 20%;" itemscope itemtype="https://schema.org/Grant">
|
||||
<div class="card mb-3 shadow" style="max-width: 80%;margin-left: 20%;">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
{% if title %}<h5 class="card-title text-end" itemprop="sponsor">{{ title | markdown | safe }}</h5>{% endif %}
|
||||
{% if title %}<h5 class="card-title text-end">{{ title | markdown | safe }}</h5>{% endif %}
|
||||
{% if icon %}
|
||||
<span class="badge bg-transparent position-absolute top-0 start-0 translate-middle-x">
|
||||
<img src="{{ icon | safe }}" alt="{{ icon }}" class="rounded"
|
||||
<img src="{{ icon }}" alt="{{ icon }}" class="rounded"
|
||||
style="height: 2rem;"/>
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="card-text" itemprop="description">{{ body | markdown | safe }}</div>
|
||||
<p class="card-text">{{ body | markdown | safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 funding-logo-end d-flex p-1 justify-content-center">
|
||||
{% if logo %}
|
||||
{% if logo_link %}<a href="{{ logo_link | safe }}" class="align-self-center" itemprop="url">{% endif %}
|
||||
<img src="{{ logo | safe }}" class="img-fluid rounded" alt="{{ logo_alt }}" itemprop="image" />
|
||||
{% if logo_link %}<a href="{{ logo_link }}" class="align-self-center">{% endif %}
|
||||
<img src="{{ logo }}" class="img-fluid rounded" alt="{{ logo_alt }}"/>
|
||||
{% if logo_link %}</a>{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -52,12 +52,12 @@
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="card mb-3 shadow-sm" style="max-width: 100%;" itemscope itemtype="https://schema.org/Poster">
|
||||
<div class="card mb-3 shadow-sm" style="max-width: 100%;">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-12">
|
||||
<div class="card-body">
|
||||
{% if title %}<h4 class="card-title text-center" itemprop="headline">{{ title | markdown | safe }}</h4>{% endif %}
|
||||
<div class="card-text" itemprop="description">{{ body | markdown | safe }}</div>
|
||||
{% if title %}<h4 class="card-title text-center">{{ title | markdown | safe }}</h4>{% endif %}
|
||||
<p class="card-text">{{ body | markdown | safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
5
templates/shortcodes/layout_html.html
Normal file
5
templates/shortcodes/layout_html.html
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="{% if css %}{{ css }}{% else %}row d-flex align-items-center mt-5 mb-5{% endif %}">
|
||||
|
||||
{{ body | safe }}
|
||||
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user