Showing preview only (1,110K chars total). Download the full file or copy to clipboard to get everything.
Repository: shoutem/shoutem.github.io
Branch: master
Commit: 0b5e153a0a1e
Files: 136
Total size: 1.0 MB
Directory structure:
gitextract_rdoazj5u/
├── .gitignore
├── Gemfile
├── README.md
├── _config.yml
├── _includes/
│ ├── cards.html
│ ├── footer.html
│ ├── ga.html
│ ├── head.html
│ ├── home-header-video.html
│ ├── navbar.html
│ ├── overview-content.html
│ ├── sidebar-nav.html
│ └── signup-modal.html
├── _layouts/
│ ├── doc.html
│ ├── home.html
│ └── overview.html
├── _sass/
│ ├── animation.scss
│ ├── base.scss
│ ├── cards.scss
│ ├── colors.scss
│ ├── documentation-layout.scss
│ ├── footer.scss
│ ├── github.scss
│ ├── helpers.scss
│ ├── menu-overlay.scss
│ ├── mixins.scss
│ ├── navbar.scss
│ ├── pager.scss
│ ├── sidebar.scss
│ ├── typography.scss
│ └── variables.scss
├── css/
│ ├── documentation.scss
│ ├── home.scss
│ ├── prism.css
│ └── style.scss
├── csv/
│ └── restaurants.csv
├── docs/
│ ├── cloud/
│ │ └── _posts/
│ │ ├── 1970-01-01-ShoutemCloud.md
│ │ └── 1970-01-02-DataSchemas.md
│ ├── extensions/
│ │ ├── my-first-extension/
│ │ │ └── _posts/
│ │ │ ├── 1970-01-01-Introduction.md
│ │ │ ├── 1970-01-03-InitializingExtension.md
│ │ │ ├── 1970-01-04-CreatingShortcutAndScreen.md
│ │ │ ├── 1970-01-05-UsingUIToolkit.md
│ │ │ ├── 1970-01-06-UsingCloudStorage.md
│ │ │ ├── 1970-01-07-WorkingWithData.md
│ │ │ └── 1970-01-08-Publish.md
│ │ ├── reference/
│ │ │ └── _posts/
│ │ │ ├── 1970-01-01-ExtensionFile.md
│ │ │ ├── 1970-01-01-Overview.md
│ │ │ ├── 1970-01-02-Platform.md
│ │ │ ├── 1970-01-03-ExtensionExports.md
│ │ │ ├── 1970-01-04-SettingsTypesInExtension.md
│ │ │ ├── 1970-01-05-ThemeVariables.md
│ │ │ └── 1970-01-06-CLI.md
│ │ └── tutorials/
│ │ └── _posts/
│ │ ├── 1970-01-01-ConnectToApi.md
│ │ ├── 1970-01-01-GettingStarted.md
│ │ ├── 1970-01-01-PublishYourApp.md
│ │ ├── 1970-01-01-SettingLocalEnvironment.md
│ │ ├── 1970-01-02-WritingATheme.md
│ │ ├── 1970-01-03-ScreenLayouts.md
│ │ ├── 1970-01-04-WritingReactSettingsPage.md
│ │ ├── 1970-01-05-Installing3rdPartyPackages.md
│ │ ├── 1970-01-06-UsingNativeModules.md
│ │ ├── 1970-01-07-ModifyingNativeProject.md
│ │ ├── 1970-01-08-ModifiyingExtensions.md
│ │ ├── 1970-01-09-FAQ.md
│ │ ├── 1970-01-10-DebugSettingsPages.md
│ │ ├── 1970-01-11-WritingHTMLSettingsPages.md
│ │ ├── 1970-01-12-SettingsPageIntro.md
│ │ ├── 1970-01-13-UsingLocalization.md
│ │ ├── 1970-01-14-SettingUpInstagram.md
│ │ ├── 1970-01-15-UsingPatchPackage.md
│ │ ├── 1970-01-16-NavigationIntroduction.md
│ │ ├── 1970-01-17-NavigationBreakingChanges.md
│ │ ├── 1970-01-18-NavigationStacks.md
│ │ └── 1970-01-19-NavigationScreenDecorators.md
│ └── ui-toolkit/
│ ├── animation/
│ │ └── _posts/
│ │ ├── 1970-01-02-Driver.md
│ │ ├── 1970-01-03-Animations.md
│ │ ├── 1970-01-03-FadeIn.md
│ │ ├── 1970-01-04-FadeOut.md
│ │ ├── 1970-01-05-ZoomIn.md
│ │ ├── 1970-01-06-ZoomOut.md
│ │ ├── 1970-01-07-Parallax.md
│ │ └── 1970-01-08-CombiningAnimations.md
│ ├── components/
│ │ └── _posts/
│ │ ├── 1970-01-01-Introduction.md
│ │ ├── 1970-01-02-Typography.md
│ │ ├── 1970-01-03-NavigationBar.md
│ │ ├── 1970-01-04-DropDownMenu.md
│ │ ├── 1970-01-05-ListView.md
│ │ ├── 1970-01-06-GridView.md
│ │ ├── 1970-01-07-Cards.md
│ │ ├── 1970-01-08-Dividers.md
│ │ ├── 1970-01-09-Rows.md
│ │ ├── 1970-01-10-Tiles.md
│ │ ├── 1970-01-11-Spinner.md
│ │ ├── 1970-01-12-Buttons.md
│ │ ├── 1970-01-13-Image.md
│ │ ├── 1970-01-14-Icons.md
│ │ ├── 1970-01-15-View.md
│ │ ├── 1970-01-16-Screen.md
│ │ ├── 1970-01-17-TouchableOpacity.md
│ │ ├── 1970-01-18-Headers.md
│ │ ├── 1970-01-19-Overlay.md
│ │ ├── 1970-01-20-Video.md
│ │ ├── 1970-01-21-Lightbox.md
│ │ ├── 1970-01-22-RichMedia.md
│ │ ├── 1970-01-22-SimpleHtml.md
│ │ ├── 1970-01-23-TextInput.md
│ │ ├── 1970-01-24-ImagePreview.md
│ │ ├── 1970-01-25-ImageGallery.md
│ │ ├── 1970-01-26-InlineGallery.md
│ │ ├── 1970-01-27-Switch.md
│ │ └── 1970-01-29-ImageBackground.md
│ └── theme/
│ └── 1970-01-01-Theme.md
├── index.html
├── jekyll-static.sh
├── js/
│ ├── animation.js
│ ├── docs.js
│ ├── flourish.js
│ ├── main.js
│ └── prism.js
├── lib/
│ └── codemirror/
│ ├── codemirror.css
│ ├── codemirror.js
│ └── mode/
│ ├── javascript/
│ │ ├── index.html
│ │ ├── javascript.js
│ │ ├── json-ld.html
│ │ ├── test.js
│ │ └── typescript.html
│ └── jsx/
│ ├── index.html
│ ├── jsx.js
│ └── test.js
├── static/
│ └── localization/
│ └── en.json
└── video/
├── examples/
│ ├── 01 parallax.webm
│ ├── 02 hero header.webm
│ ├── 03 fade in and out.webm
│ └── 04 zoom in and out.webm
├── header.webm
└── header@2x.webm
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
### OSX ###
.DS_Store
### Jekyll ###
_site/
.sass-cache/
.jekyll-metadata
### Sublime ###
*.sublime-*
### WebStorm ###
.idea
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
gem 'github-pages', group: :jekyll_plugins
gem 'wdm', '>= 0.1.0' if Gem.win_platform?
================================================
FILE: README.md
================================================
# Shoutem
Shoutem is a platform for building beautiful React Native mobile apps. The easiest way to understand what Shoutem is to think of it as the WordPress for mobile apps. Apps are divided into smaller modules, called `extensions`. On WordPress you build a website out of plugins; on Shoutem you build a mobile app out of extensions. This architecture helps us achieve big development efficiency.
## Community
Join [our community](https://www.facebook.com/groups/shoutem.community/) on Facebook. Also, feel free to ask a question on Stack Overflow using ["shoutem" tag](http://stackoverflow.com/tags/shoutem).
## Feature requests
We would like to know what you want to do with Shoutem extensions. Feel free to raise issues with `feature requests` and upvote the ones labeled as a `feature request`, so we can know what you need the most. We're going to put label `in progress` for the features that we decide to implement.
## React Native
Shoutem app is a React Native app. We designed extensions’ architecture with the goal not to add overhead over pure React Native. If you know React Native, you will know how to write Shoutem extensions. If you're just starting with React Native, check out our [React Native School](https://school.shoutem.com/).
## Marketplace
Shoutem prepared and open sourced a lot of extensions that you can use in your apps. Don’t reinvent the wheel: reuse extensions which are suitable for you, customize them or create new ones. You can find the full list of extensions [here](https://github.com/shoutem/extensions).
## Customization
All our extensions are open sourced, so you can modify anything you want by simply forking them. You can also write your own ones from scratch.
## Tools and docs
We've prepared tools to speed up your extension development like [Shoutem CLI](https://shoutem.github.io/docs/extensions/reference/cli), [Shoutem UI toolkit](https://shoutem.github.io/ui/) for faster component development and documented all the concepts on this [developer's portal](http://shoutem.github.io/).
If you find some bug or have a suggestion on how to improve anything, feel free to contribute or raise an issue!
## Start creating your apps!
If you’re not already using Shoutem, you can create your account [here](https://new.shoutem.com), and start developing beautiful mobile apps.
================================================
FILE: _config.yml
================================================
title: Shoutem Developers
description: Supercharge your React Native development with Shoutem
tracking_id: UA-807293-5
repository: shoutem/shoutem.github.io
sass:
style: compressed
# Build settings
markdown: kramdown
kramdown:
syntax_highlighter_opts:
disable : true
url: "https://shoutem.github.io"
# Variables
shoutem:
builderURL: "https://builder.shoutem.com"
previewAppiOS: "https://itunes.apple.com/us/app/shoutem-preview/id1211732978"
previewAppAndroid: "https://play.google.com/store/apps/details?id=com.shoutem.extensions.preview"
cli: "https://www.npmjs.com/package/@shoutem/cli"
school: "http://school.shoutem.com/"
pricing: "https://new.shoutem.com/pricing/"
support: "https://new.shoutem.com/support/"
external:
appleAppStore: "https://itunes.apple.com/ca/genre/ios/id36?mt=8"
appleDeveloperAccount: "https://developer.apple.com/programs/enroll/"
googlePlayStore: "https://play.google.com/store"
googlePlayDeveloperAccount: "https://play.google.com/apps/publish/signup/"
xcode: "https://developer.apple.com/xcode/"
androidStudio: "https://developer.android.com/studio/index.html"
# Examples
example:
devName: tom
devEmail: tom@shoutem.com
extensionName: restaurants
appId: 8074
qrCodeLink: http://shoutem.app.link/?host=df4d8960.ngrok.io&port=80&dev=false
================================================
FILE: _includes/cards.html
================================================
<div class="section-content">
<div class="container screen-type-cards">
<h4 class="subtitle">40+ full-featured extensions</h4>
<div id="screen-type-cards" class="dragdealer">
<div class="handle" style="width: 4042px;">
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-main-navigation-image.svg" alt="">
<h5 class="card-title">Main navigation</h5>
<p>App-level navigation</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-news-image.svg" alt="">
<h5 class="card-title">News</h5>
<p>Show news articles</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-events-image.svg" alt="">
<h5 class="card-title">Events</h5>
<p>Show items with location and time</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-products-image.svg" alt="">
<h5 class="card-title">Products</h5>
<p>Show products with purchase link</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-photos-image.svg" alt="">
<h5 class="card-title">Photos</h5>
<p>Show a photo gallery</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-videos-image.svg" alt="">
<h5 class="card-title">Videos</h5>
<p>Show a video gallery</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-about-image.svg" alt="">
<h5 class="card-title">About</h5>
<p>Show info about your app or your business</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-places-image.svg" alt="">
<h5 class="card-title">Places</h5>
<p>Show items with location</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-menu-items-image.svg" alt="">
<h5 class="card-title">Restaurant menu</h5>
<p>Show a restaurant menu</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-books-image.svg" alt="">
<h5 class="card-title">Books</h5>
<p>Show books and authors</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-news-rss-image.svg" alt="">
<h5 class="card-title">RSS News</h5>
<p>Show news articles from RSS feed</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-people-image.svg" alt="">
<h5 class="card-title">People</h5>
<p>Show people and contact details</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-web-page-image.svg" alt="">
<h5 class="card-title">Web view</h5>
<p>Show a web page in-app or in a browser</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-favorites-image.svg" alt="">
<h5 class="card-title">Favorites</h5>
<p>Extensions that are using Shoutem Favorites extensions are able to store and retrieve items that that app user has bookmarked in local app storage. </p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-folder-image.svg" alt="">
<h5 class="card-title">Navigation</h5>
<p>Shows sub-navigation for the nested screen</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-radio-image.svg" alt="">
<h5 class="card-title">Radio</h5>
<p>Stream a radio station</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-videos-rss-image.svg" alt="">
<h5 class="card-title">RSS Videos</h5>
<p>Show a video gallery from RSS feed</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-videos-vimeo-image.svg" alt="">
<h5 class="card-title">Vimeo videos</h5>
<p>Show Vimeo video gallery</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/extensions/add-videos-youtube-image.svg" alt="">
<h5 class="card-title">YouTube videos</h5>
<p>Show YouTube video gallery</p>
</div>
</div>
</div>
</div>
</div>
<div class="container layout-cards">
<h4 class="subtitle">Mobile backend services</h4>
<div id="layout-cards" class="dragdealer">
<div class="handle" style="width: 2330px;">
<div class="card">
<div class="inner">
<img src="img/cards/services/add-analytics-image.svg" alt="">
<h5 class="card-title">Analytics</h5>
<p>Track Shoutem events</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-authentication-image.svg" alt="">
<h5 class="card-title">User authentication</h5>
<p>Show user profile, sign out user</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-cms-image.svg" alt="">
<h5 class="card-title">CMS</h5>
<p>Shoutem CMS extension</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-code-push-image.svg" alt="">
<h5 class="card-title">Code push</h5>
<p>Provides CodePush support for over the air code updates</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-firebase-image.svg" alt="">
<h5 class="card-title">Firebase</h5>
<p>Firebase integration for sending push notifications, storage…</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-google-analytics-image.svg" alt="">
<h5 class="card-title">Google Analytics</h5>
<p>Enables Google Analytics</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-layouts-image.svg" alt="">
<h5 class="card-title">Layouts</h5>
<p>Shoutem Layout extension</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-push-notifications-image.svg" alt="">
<h5 class="card-title">Push notifications</h5>
<p>Base extension for push notifications</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-rss-image.svg" alt="">
<h5 class="card-title">RSS feeds</h5>
<p>Shoutem RSS extension</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-shopify-image.svg" alt="">
<h5 class="card-title">Shopify</h5>
<p>Shopify integration</p>
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/services/add-theme-image.svg" alt="">
<h5 class="card-title">Themes</h5>
<p>Resolve and store theme related configuration</p>
</div>
</div>
</div>
</div>
</div>
<div class="container visual-style-cards">
<h4 class="subtitle">Carefully crafted themes for you to use and customize</h4>
<div id="visual-style-cards" class="dragdealer">
<div class="handle" style="width: 1270px;">
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-1.jpg"
srcset="img/cards/themes/theme-1.jpg 1x,
img/cards/themes/theme-1@2x.jpg 2x" alt="" />
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-2.jpg"
srcset="img/cards/themes/theme-2.jpg 1x,
img/cards/themes/theme-2@2x.jpg 2x" alt="" />
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-3.jpg"
srcset="img/cards/themes/theme-3.jpg 1x,
img/cards/themes/theme-3@2x.jpg 2x" alt="" />
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-4.jpg"
srcset="img/cards/themes/theme-4.jpg 1x,
img/cards/themes/theme-4@2x.jpg 2x" alt="" />
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-5.jpg"
srcset="img/cards/themes/theme-5.jpg 1x,
img/cards/themes/theme-5@2x.jpg 2x" alt="" />
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-6.jpg"
srcset="img/cards/themes/theme-6.jpg 1x,
img/cards/themes/theme-6@2x.jpg 2x" alt="" />
</div>
</div>
<div class="card">
<div class="inner">
<img src="img/cards/themes/theme-7.jpg"
srcset="img/cards/themes/theme-7.jpg 1x,
img/cards/themes/theme-7@2x.jpg 2x" alt="" />
</div>
</div>
</div>
</div>
</div>
</div>
================================================
FILE: _includes/footer.html
================================================
<footer class="footer">
<div id="signup" class="signup">
<div class="signup-title">Stay informed</div>
<div class="signup-description">Subscribe to our newsletter if you'd like to stay informed about Shoutem Extensions and Shoutem UI Toolkit.</div>
<!-- Begin MailChimp Signup Form -->
<div id="mc_embed_signup">
<form action="//shoutem.us1.list-manage.com/subscribe/post?u=2465033d224d289aca631307f&id=8c16387120" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<div>
<input type="email" spellcheck="false" value="" name="EMAIL" class="signup-email required email" placeholder="Enter your email" id="mce-EMAIL">
<input type="submit" value="Sign up" name="subscribe" id="mc-embedded-subscribe" class="signup-button">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div>
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2465033d224d289aca631307f_8c16387120" tabindex="-1" value=""></div>
<div class="clear"></div>
</div>
</form>
</div>
<!--End mc_embed_signup-->
</div>
<div class="bottom-footer">
<div class="container-fluid">
<a href="https://www.shoutem.com" class="footer-logo"></a>
<ul class="footer-menu">
<li><a href="https://www.shoutem.com/about">About</a></li>
<li><a href="https://www.shoutem.com/terms-of-service">Terms of service</a></li>
<li><a href="https://www.shoutem.com/about/contact-us">Contact us</a></li>
<li class="icon-github"><a href="https://github.com/shoutem/shoutem.github.io">Github</a></li>
<li class="icon-twitter"><a href="https://twitter.com/shoutem">Twitter</a></li>
</ul>
</div>
</div>
</footer>
================================================
FILE: _includes/ga.html
================================================
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{{ site.tracking_id }}', 'auto');
ga('send', 'pageview');
</script>
================================================
FILE: _includes/head.html
================================================
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>
{% if page.title %}
{{ page.title }}
{% if page.section %} - {{ page.section }}{% endif %}
-
{% endif %}
{{ site.title }}
</title>
<meta name="description" content="{{ site.description }}">
<link rel="stylesheet" type="text/css" href="{{ site.baseurl }}/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="{{ site.baseurl }}/css/style.css">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="{{ site.url }}/img/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="76x76" href="{{ site.url }}/img/apple-touch-icon-ipad.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ site.url }}/img/apple-touch-icon@2x.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ site.url }}/img/apple-touch-icon-ipad@2x.png">
<link rel="apple-touch-icon" sizes="180x180" href="{{ site.url }}/img/apple-touch-icon@3x.png">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<meta property="og:title" content="{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}" />
<meta property="og:image" content="{{ site.url }}/img/og-image.jpg" />
<meta property="og:description" content="{{ site.description }}" />
<meta property="og:url" content="{% if page.url %}{{ page.url }}{% else %}{{ site.url }}{% endif %}">
<meta name="twitter:card" content="summary" />
<meta name="twitter:description" content="{{ site.description }}" />
<meta name="twitter:title" content="{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}" />
<meta name="twitter:site" content="@shoutem" />
<meta name="twitter:image" content="{{ site.url }}/img/twitter-card-image.png" />
<!-- Hotjar Tracking Code for http://shoutem.com -->
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:211696,hjsv:5};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'//static.hotjar.com/c/hotjar-','.js?sv=');
</script>
<script src="https://cdn.optimizely.com/js/8386530091.js"></script>
================================================
FILE: _includes/home-header-video.html
================================================
<video autoplay loop class="video-screen">
<source src="/video/header.webm" type="video/webm" />
<source src="/video/header.mp4" type="video/mp4" />
</video>
<script>
var vc = document.querySelector(".video-screen");
var vcParent = vc.parentElement;
var temp;
if( window.matchMedia )
{
var mq = window.matchMedia("only screen and (-webkit-min-device-pixel-ratio: 2),only screen and ( min--moz-device-pixel-ratio: 2), only screen and ( o-min-device-pixel-ratio: 2/1), only screen and ( min-device-pixel-ratio: 2), only screen and ( min-resolution: 192dpi), only screen and ( min-resolution: 2dppx)");
if( mq && mq.matches )
{
vc.pause();
vcParent.removeChild(vc);
vc = document.createElement("video");
vc.className = "video-screen";
vc.setAttribute("autoplay", true);
vc.setAttribute("loop", true);
vcParent.appendChild(vc);
temp = document.createElement("source");
temp.setAttribute("src", "/video/header@2x.webm");
temp.setAttribute("type", "video/webm");
vc.appendChild(temp);
temp = document.createElement("source");
temp.setAttribute("src", "/video/header@2x.mp4");
temp.setAttribute("type", "video/mp4");
vc.appendChild(temp);
vc.play();
}
}
</script>
================================================
FILE: _includes/navbar.html
================================================
<nav class="navbar navbar-default navbar-fixed-top headroom">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
{% if .page.title %}
<a class="navbar-brand" href="{{ site.baseurl }}/">
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="documentation-logo" transform="translate(-31.000000, -31.000000)" fill="#00AADF">
<path d="M32.7621883,49 C32.1600337,49 31.5861304,48.6779372 31.2640665,48.1589238 C30.9484601,47.6520179 30.9145586,47.0296861 31.1688196,46.4864574 L37.2323381,32.0380269 C37.2404099,32.0186547 37.2492888,31.9992825 37.2581678,31.9807175 C37.5576307,31.3753363 38.1646283,31 38.840236,31 C39.3100134,31 39.7523467,31.1832287 40.0857111,31.5165919 L48.4835874,39.9144395 C48.8839475,40.3156054 49.0687912,40.8878924 48.9767729,41.4472646 C48.8839475,42.0066368 48.5255606,42.4901345 48.0178459,42.7411659 C47.9992808,42.7500448 47.9799085,42.7589238 47.9613434,42.7669955 L33.5136706,48.8304933 C33.2739389,48.9426906 33.0212923,49 32.7621883,49 L32.7621883,49 Z M38.9274112,33.2124664 L33.2448805,46.7544395 L46.7868999,41.0711211 L38.9274112,33.2124664 L38.9274112,33.2124664 Z" id="ic-shoutem-mark"></path>
</g>
</svg>
</a>
{% else %}
<a class="navbar-brand" href="http://new.shoutem.com">
<span class="navbar-brand--logo">Shoutem</span>
</a>
{% endif %}
</a>
{% if .page.title %}
<ol class="breadcrumb navbar-left">
<li class="page-section">{{ page.section }}</li>
<li class="page-title">{{ page.title }}</li>
</ol>
{% endif %}
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
{% if .page.title %}
<li>
<form class="navbar-search-form navbar-form navbar-right navbar-form-border" role="search">
<div class="form-group">
<img class="search-icon" src="{{ site.baseurl }}/img/ui-toolkit/icons/search.png" />
<input id="navbar-search-input" placeholder="Search..." type="text">
</div>
</form>
</li>
<li class="edit-link"><a href="https://github.com/shoutem/shoutem.github.io/blob/master/{{ page.path }}">Edit this page</li>
{% endif %}
<li id="documentationTab"><a class="navbar-nav--a--link" href="{{ site.baseurl }}/docs/extensions/tutorials/getting-started">Documentation</a></li>
<li id="signup-button"><a href="#signup" class="btn btn-primary">Stay informed</a></li>
</ul>
</div>
</div>
</nav>
================================================
FILE: _includes/overview-content.html
================================================
<h1>Develop native apps with Shoutem and React Native</h1>
<div class="section section-intro">
<div class="intro-text">
<div class="intro-title">
Build apps with Shoutem extensions
</div>
<div class="intro-content">
<p>Shoutem apps are made of extensions. We’ve prepared 40+ extensions
that cover most of the things you’ll need in any app. Customize them,
or create literally anything by writing a new extension.</p>
<img src="{{ site.baseurl }}/img/overview/js-rn-logos@2x.png" alt="Javascript + React Native" width="189" height="32" />
<p><small>P.S. CLI, app publishing, cloud storage and more, included.</small></p>
</div>
</div>
</div>
<div class="section section-tutorials">
<h2>Get started with tutorials</h2>
<div class="list-cards">
<a class="card rounded shadowed" href="{{ site.baseurl }}/docs/extensions/getting-started/introduction">
<div class="card-image">
<img src="{{ site.baseurl }}/img/overview/icon-tutorial-getting-started.svg" alt="">
</div>
<div class="card-content">
<div class="card-category">Tutorial</div>
<div class="card-title">Get started</div>
<p>Create your first Shoutem extension</p>
<div class="card-time">
<span class="icon-time"></span> 45 min
<span class="difficulty">simple</span>
</div>
</div>
</a>
<a class="card rounded shadowed" href="{{ site.baseurl }}/docs/extensions/tutorials/modifying-extensions">
<div class="card-image extensions">
<img src="{{ site.baseurl }}/img/overview/puzzle-wrench.svg" alt="">
</div>
<div class="card-content">
<div class="card-category">Tutorial</div>
<div class="card-title">Modifying extensions</div>
<p>Learn how to customize Shoutem extensions</p>
<div class="card-time">
<span class="icon-time"></span> 30 min
<span class="difficulty">simple</span>
</div>
</div>
</a>
<a class="card rounded shadowed" href="{{ site.baseurl }}/docs/extensions/tutorials/writing-a-theme">
<div class="card-image themes">
<img src="{{ site.baseurl }}/img/overview/paintbrush.svg" alt="">
</div>
<div class="card-content">
<div class="card-category">Tutorial</div>
<div class="card-title">Creating themes</div>
<p>Learn how to style your app using themes</p>
<div class="card-time">
<span class="icon-time"></span> 30 min
<span class="difficulty">simple</span>
</div>
</div>
</a>
</div>
<div class="list-cards row-2">
<a class="card rounded shadowed" href="{{ site.baseurl }}/docs/extensions/tutorials/using-native-api">
<div class="card-content">
<div class="card-category">Tutorial</div>
<div class="card-title">Using native code, native SKDs and JS libraries</div>
<div class="card-time">
<span class="icon-time"></span> 20 min
<span class="difficulty">simple</span>
</div>
</div>
</a>
<a class="card rounded shadowed" href="{{ site.baseurl }}/docs/extensions/tutorials/using-native-api">
<div class="card-content">
<div class="card-category">Tutorial</div>
<div class="card-title">Creating an integation with a third-party service</div>
<div class="card-time">
<span class="icon-time"></span> 20 min
<span class="difficulty">simple</span>
</div>
</div>
</a>
<a class="card rounded shadowed" href="{{ site.baseurl }}/docs/extensions/getting-started/publish">
<div class="card-content">
<div class="card-category">Tutorial</div>
<div class="card-title">Shoutem automated and manual app publishing</div>
<div class="card-time">
<span class="icon-time"></span> 10 min
<span class="difficulty">simple</span>
</div>
</div>
</a>
</div>
</div>
<div class="section section-devstack">
<h2>Development stack</h2>
<h3>JavaScript and React Native only</h3>
<p>Extensions are written in modern JavaScript and React Native.
Use an editor of your choice and start coding!<br />
Not a JavaScript and React Native developer?
Get a quick-start with our <a href="{{ site.baseurl }}/docs/extensions/getting-started/introduction">tutorial</a>.</p>
<ul class="logotypes">
<li class="javascript">Javascript</li>
<li class="react-native">React Native</li>
<li class="shoutem-ui-toolkit">Shoutem UI Toolkit</li>
<li class="code-editor">Any code editor</li>
</ul>
</div>
<div class="section section-faq">
<h2>FAQ</h2>
<dl>
<dt>What do I need on my system?</dt>
<dd>Just a text editor and terminal (Windows, macOS or Linux). You
don’t need XCode or Android Studio, and you don’t need a Mac to
build iOS apps.</dd>
<dt>How much advanced JavaScript knowledge is required?</dt>
<dd>Just a text editor and terminal (Windows, macOS or Linux). You
don’t need XCode or Android Studio, and you don’t need a Mac to
build iOS apps.</dd>
<dt>Does Shoutem build and publish my apps?</dt>
<dd>Just a text editor and terminal (Windows, macOS or Linux). You
don’t need XCode or Android Studio, and you don’t need a Mac to
build iOS apps.</dd>
<dt>Do I need to pay for this?</dt>
<dd>Just a text editor and terminal (Windows, macOS or Linux). You
don’t need XCode or Android Studio, and you don’t need a Mac to
build iOS apps.</dd>
</dl>
</div>
================================================
FILE: _includes/sidebar-nav.html
================================================
<ul class="sidebar-nav navigation level-1" id="menu">
<li class="hidden-md hidden-lg home-link"><a href="/">Shoutem Developers</a></li>
<!-- Getting started -->
<li>
<a class="menu-group-title" href="{{ site.baseurl }}/docs/extensions/tutorials/getting-started">Getting started</a>
</li>
<!-- My first extension -->
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/introduction">My first extension</a></li>
<ul id="extensions" class="menu-group-wrapper level-2">
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/introduction">Introduction</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/initializing-extension">Creating an extension</a>
</li>
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/shortcut-and-screen">Creating screens and
shortcuts</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/using-ui-toolkit">Using UI Toolkit</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/using-cloud-storage">Using Cloud Storage</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/working-with-data">Working with data</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/my-first-extension/publish">Publishing</a></li>
</ul>
</li>
<!-- Guides -->
<li><a class="menu-group-title"
href="{{ site.baseurl }}/docs/extensions/tutorials/setting-local-environment">Guides</a>
<ul id="extensions-tutorials" class="menu-group-wrapper level-2">
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/setting-local-environment">Local environment setup</a>
</li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/publish-your-app">Publish your app</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/connecting-to-api">Connecting to 3rd party API</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/installing-3rd-party-packages">Installing 3rd party
packages</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/using-patch-package">Using patch-package</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/modifying-native-project">Modifying native project</a>
</li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/writing-a-theme">Writing a theme</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/screen-layouts">Screen layouts</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/using-localization">Localization</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/setting-up-instagram">Setting up Instagram</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/modifying-extensions">Modifying extensions</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/using-native-api">Using native API</a></li>
<li>
<a class="menu-group-title"
href="{{ site.baseurl }}/docs/extensions/tutorials/settings-pages-introduction">Settings pages</a>
<ul id="ui-toolkit-components" class="menu-group-wrapper level-3">
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/settings-pages-introduction">Introduction</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/writing-react-settings-page">Writing React settings
pages</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/writing-html-settings-page">Writing HTML settings
pages</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/debug-settings-pages">Local development</a></li>
</ul>
</li>
<li>
<a class="menu-group-title"
href="{{ site.baseurl }}/docs/extensions/tutorials/navigation-introduction">Navigation guides</a>
<ul id="ui-toolkit-components" class="menu-group-wrapper level-3">
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/navigation-introduction">Introduction</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/navigation-breaking-changes">Breaking changes</a>
</li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/navigation-stacks">Navigation stacks</a>
</li>
<li><a href="{{ site.baseurl }}/docs/extensions/tutorials/navigation-screen-decorators">Screen decorators</a>
</li>
</ul>
</li>
</ul>
</li>
<!-- Reference -->
<li>
<a class="menu-group-title" href="{{ site.baseurl }}/docs/extensions/reference/overview">Reference</a>
<ul id="extensions-tutorials" class="menu-group-wrapper level-2">
<li><a href="{{ site.baseurl }}/docs/extensions/reference/overview">Technical Overview</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/reference/extension">Extension file format</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/reference/platform">Platform</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/reference/extension-exports">Extension exports</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/reference/settings-types">Settings types</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/reference/theme-variables">Theme variables</a></li>
<li><a href="{{ site.baseurl }}/docs/extensions/reference/cli">CLI</a></li>
</ul>
</li>
<!-- UI toolkit -->
<li>
<a class="menu-group-title" href="{{ site.baseurl }}/docs/ui-toolkit/introduction">Shoutem UI toolkit</a>
<ul id="ui-toolkit" class="menu-group-wrapper level-2">
<!-- Introduction -->
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/introduction">Introduction</a></li>
<!-- Components -->
<li>
<a class="menu-group-title" href="{{ site.baseurl }}/docs/ui-toolkit/components/typography">Components</a>
<ul id="ui-toolkit-components" class="menu-group-wrapper level-3">
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/typography">Typography</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/navigation-bar">NavigationBar</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/dropdown-menu">DropDownMenu</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/list-view">ListView</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/grid-view">GridView</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/cards">Cards</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/dividers">Dividers</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/rows">Rows</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/tiles">Tiles</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/spinner">Spinner</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/buttons">Buttons</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/switch">Switch</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/image">Image</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/image-background">ImageBackground</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/image-preview">ImagePreview</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/inline-gallery">InlineGallery</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/image-gallery">ImageGallery</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/icons">Icons</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/view">View</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/screen">Screen</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/touchable-opacity">TouchableOpacity</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/touchable">Touchable</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/headers">Headers</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/overlay">Overlay</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/text-input">TextInput</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/video">Video</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/lightbox">Lightbox</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/html">HTML</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/components/simplehtml">SimpleHtml</a></li>
</ul>
</li>
<!-- Theme -->
<li>
<a href="{{ site.baseurl }}/docs/ui-toolkit/theme/introduction">Theme</a>
</li>
<!-- Animation -->
<li>
<a class="menu-group-title" href="{{ site.baseurl }}/docs/ui-toolkit/animation/introduction">Animation</a>
<ul id="ui-toolkit-animation" class="menu-group-wrapper level-3">
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/introduction">Introduction</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/driver">Driver</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/fade-in">FadeIn</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/fade-out">FadeOut</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/zoom-in">ZoomIn</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/zoom-out">ZoomOut</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/parallax">Parallax</a></li>
<li><a href="{{ site.baseurl }}/docs/ui-toolkit/animation/combining-animations">Combining Animations</a></li>
</ul>
</li>
</ul>
</li>
<!-- Shoutem Cloud -->
<li>
<a class="menu-group-title" href="{{ site.baseurl }}/docs/cloud/introduction">Shoutem Cloud</a>
<ul id="ui-toolkit" class="menu-group-wrapper level-2">
<!-- Introduction -->
<li><a href="{{ site.baseurl }}/docs/cloud/introduction">Introduction</a></li>
<li><a href="{{ site.baseurl }}/docs/cloud/data-schemas">Data Schemas Reference</a></li>
</ul>
</li>
<!-- FAQ -->
<li><a class="menu-group-title" href="{{ site.baseurl }}/docs/extensions/tutorials/faq">FAQ</a></li>
<li id="signup-button-menu" class="menu-button-signup hidden-md hidden-lg"><a href="#signup"
class="button-signup">Stay informed</a></li>
<li class="sidelink hidden-md hidden-lg"><a href="http://new.shoutem.com">Shoutem home</a></li>
<li class="sidelink hidden-md hidden-lg"><a href="http://new.shoutem.com/about">About</a></li>
<li class="sidelink hidden-md hidden-lg"><a href="http://new.shoutem.com/terms-of-service">Terms of service</a></li>
<li class="sidelink hidden-md hidden-lg"><a href="http://new.shoutem.com/about/contact-us">Contact us</a></li>
</ul>
================================================
FILE: _includes/signup-modal.html
================================================
<div id="signup-modal" role="dialog" aria-labelledby="dialog-title" aria-describedby="dialog-description">
<div class="content">
<div class="signup-title" id="dialog-title">Stay informed</div>
<div class="signup-description" id="dialog-description">Subscribe to our newsletter if you'd like to stay informed about Shoutem Extensions and Shoutem UI Toolkit.</div>
<!-- Begin MailChimp Signup Form -->
<div id="mc_embed_signup">
<form action="//shoutem.us1.list-manage.com/subscribe/post?u=2465033d224d289aca631307f&id=8c16387120" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<label for="mce-EMAIL">Enter your email*</label>
<input type="email" spellcheck="false" value="" name="EMAIL" class="signup-email required email form-control" placeholder="Enter your email" id="mce-EMAIL">
<div class="control-buttons">
<input type="button" value="Cancel" name="cancel" id="mc-embedded-cancel" class="cancel-button"><input type="submit" value="Sign up" name="subscribe" id="mc-embedded-subscribe" class="signup-button">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div>
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2465033d224d289aca631307f_8c16387120" tabindex="-1" value=""></div>
<div class="clear"></div>
</div>
</form>
</div>
<!--End mc_embed_signup-->
</div>
</div>
================================================
FILE: _layouts/doc.html
================================================
<!DOCTYPE html>
<html>
{% include head.html %}
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/prism.css">
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/documentation.css">
<!-- <link rel="stylesheet" type="text/css" href="{{ site.url }}/lib/codemirror/codemirror.css"> -->
</head>
<body class="documentation">
{% include navbar.html %}
<div id="sidebar-wrapper">
{% include sidebar-nav.html %}
</div>
<div id="wrapper">
<div class="documentation-wrapper">
<div id="documentation">
<!-- It will work both on page and github markdown -->
{{ content }}
</div>
</div>
<nav id="pager-wrapper">
<ul class="pager">
<li class="previous inactive"><a href="#"><span>Previous</span></a></li>
<li class="next inactive"><a href="#"><span>Next</span></a></li>
</ul>
</nav>
</div>
<div class="mobile-menu-overlay">
<div class="close-menu-overlay">
</div>
</div>
{% include signup-modal.html %}
<!-- <script src="{{ site.url }}/lib/codemirror/codemirror.js"></script>
<script src="{{ site.url }}/lib/codemirror/mode/javascript/javascript.js"></script>
<script src="{{ site.url }}/lib/codemirror/mode/jsx/jsx.js"></script> -->
<script src="https://code.jquery.com/jquery-2.2.3.min.js" integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo=" crossorigin="anonymous"></script>
<script type="text/javascript" src="{{ site.url }}/js/headroom.min.js"></script>
<script src="{{ site.url }}/js/flourish-0.3.2.min.js"></script>
<script src="{{ site.url }}/js/main.js"></script>
<script src="{{ site.url }}/js/docs.js"></script>
<script src="{{ site.url }}/js/prism.js" data-manual></script>
{% include ga.html %}
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script type="text/javascript"> docsearch({
apiKey: '6b283baa7ff0e8454787d13149aa3a77',
indexName: 'shoutem',
inputSelector: '#navbar-search-input',
debug: false
});
</script>
</body>
</html>
================================================
FILE: _layouts/home.html
================================================
<!DOCTYPE html>
<html lang="en-us">
{% include head.html %}
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/home.css">
</head>
<body class="home">
{% include navbar.html %}
<div id="wrapper" class="home">
<div class="container-fluid">
<div class="header row">
<div class="main col-xs-12">
<div class="shoutem-ani"></div>
<div class="header-wrapper">
<div class="header-text">
<h1 class="main-heading">A platform to build, publish, and manage native apps with ease</h1>
<h2 class="sub-heading">Build beautiful native apps with <strong>Shoutem</strong> and <strong>React Native</strong> in no time</h2>
<div class="os-icons">
<img src="img/ic_apple.svg" alt="" class="icon-apple" />
<img src="img/ic_android.svg" alt="" class="icon-android" />
</div>
</div>
</div>
</div>
</div>
<div class="row blue-background section-step0 with-arrow">
<div class="section narrow">
<h2 class="section-header section-header--big">How does it work?</h2>
<ol class="flex-container steps">
<li>
<div class="step-title">1. Get a head start</div>
<div class="step-button arrow-box"><span class="center-v">Backend, 40+ extensions, push, analytics</span></div>
</li>
<li>
<div class="step-title">2. Customize</div>
<div class="step-button arrow-box"><span class="center-v">No limits, powered by React Native</span></div>
</li>
<li>
<div class="step-title">3. Automate maintenance</div>
<div class="step-button publish-button"><span class="center-v">Automated publishing and updates</span></div>
</li>
</ol>
<div class="section-footer">When was the last time you’ve heard that someone built eCommerce website from scratch? Hm…, like never. So why would you build an app from scratch?</div>
</div>
</div>
<div class="row section-customization">
<div class="section wider">
<h2 class="section-header">
Get a head start with Shoutem extensions
</h2>
{% include cards.html %}
</div>
</div>
<div class="row blue-background section-step2">
<div class="section wider">
<h2 class="section-header">
Customize without limits, it’s open source
</h2>
<div class="row">
<div class="col-sm-7 col-sm-offset-1 image-rn-code">
<img src="img/image-rn-code-combined@2x.png" alt="">
</div>
<ul class="col-md-4 bullet-points">
<li><span class="bullet-icon a"></span><span class="bullet-text">Customize our extensions or create your own</span></li>
<li><span class="bullet-icon b"></span><span class="bullet-text">Code, test and debug locally</span></li>
<li><span class="bullet-icon a"></span><span class="bullet-text react-native">100% JavaScript and <a href="https://facebook.github.io/react-native/" target="_blank">React Native <span class="bullet-icon react"></span></a></span></li>
<li><span class="bullet-icon a"></span><span class="bullet-text">Extensive documentation and developer tools</span></li>
</ul>
</div>
</div>
</div>
<div class="row section-step3">
<div class="section narrow">
<h2 class="section-header">Publish and update regularly with ease</h2>
<h3 class="section-sub-header">A CMS backend anyone can use</h3>
</div>
<div class="section fullwidth">
<div class="col col-md-5 text">
<div class="bullets-wrapper">
<div class="bullet-points">
<div class="bullet bullet-publish">
<h4>Publish to stores</h4>
<p>Publish automatically to App Store and Google Play</p>
</div>
<div class="bullet bullet-manage">
<h4>Manage content</h4>
<p>Grant moderator access to content managers</p>
</div>
<div class="bullet bullet-analyse">
<h4>Analyse and engage</h4>
<p>Analyze usage. Send push notifications</p>
</div>
</div>
</div>
</div>
<div class="col col-md-7 carousel">
<div class="images">
<img src="img/builder-shots/builder-shot-1.jpg"
srcset="img/builder-shots/builder-shot-1.jpg 1x,
img/builder-shots/builder-shot-1@2x.jpg 2x" />
</div>
</div>
</div>
</div>
<div class="row section-step4">
<div class="section section-featured">
<h2 class="section-header section-header--big">Featured Shoutem apps</h2>
<p>Shoutem apps are slick and fast. Best if experienced first-hand!</p>
<div class="featured-apps">
<a href="https://itunes.apple.com/us/app/brides-wedding-genius-5-1/id426415850" target="_blank">
<img src="img/featured/brides.png" alt="" />
<p>Brides Wedding Genius</p>
</a>
</div>
</div>
</div>
</div>
</div>
<div id="mobile-menu-overlay" class="mobile-menu-overlay">
<div class="close-menu-overlay"></div>
<ul class="navigation">
<li class="primary active"><a href="/">Home</a></li>
<li class="primary"><a href="{{ site.url }}/docs/extensions/tutorials/getting-started">Getting started</a></li>
<li class="primary"><a href="{{ site.url }}/docs/extensions/my-first-extension/introduction">My first extension</a></li>
<li class="primary"><a href="{{ site.url }}/docs/extensions/tutorials/setting-local-environment">Basics</a></li>
<li class="primary"><a href="{{ site.url }}/docs/extensions/tutorials/writing-settings-page">Advanced</a></li>
<li class="primary"><a href="{{ site.url }}/docs/extensions/reference/overview">Reference</a></li>
<li class="primary"><a href="{{ site.url }}/docs/ui-toolkit/introduction">Shoutem UI toolkit</a></li>
<li class="primary"><a href="{{ site.url }}/docs/cloud/introduction">Shoutem Cloud</a></li>
<li id="signup-button-menu" class="menu-button-signup"><a href="#signup" class="button-signup">Stay informed</a></li>
<li><a href="https://www.shoutem.com">Shoutem home</a></li>
<li><a href="https://www.shoutem.com/about">About</a></li>
<li><a href="https://www.shoutem.com/terms-of-service">Terms of service</a></li>
<li><a href="https://www.shoutem.com/about/contact-us">Contact us</a></li>
</ul>
<span class="pager"></span>
</div>
{% include footer.html %}
<script src="https://code.jquery.com/jquery-3.2.0.min.js" integrity="sha256-JAW99MJVpJBGcbzEuXk4Az05s/XyDdBomFqNlM3ic+I=" crossorigin="anonymous"></script>
<script src="{{ site.url }}/js/animation.js"></script>
<script src="{{ site.url }}/js/headroom.min.js"></script>
<script src="{{ site.url }}/js/dragdealer.min.js"></script>
<script src="{{ site.url }}/js/main.js"></script>
{% include ga.html %}
</body>
</html>
================================================
FILE: _layouts/overview.html
================================================
<!DOCTYPE html>
<html>
{% include head.html %}
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/prism.css">
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/documentation.css">
<!-- <link rel="stylesheet" type="text/css" href="{{ site.url }}/lib/codemirror/codemirror.css"> -->
</head>
<body class="documentation overview">
{% include navbar.html %}
<div id="sidebar-wrapper">
{% include sidebar-nav.html %}
</div>
<div id="wrapper">
<div class="documentation-wrapper">
<div id="documentation">
<!-- It will work both on page and github markdown -->
{% include overview-content.html %}
</div>
</div>
<nav id="pager-wrapper">
<ul class="pager">
<li class="previous inactive"><a href="#"><span>Previous</span></a></li>
<li class="next inactive"><a href="#"><span>Next</span></a></li>
</ul>
</nav>
</div>
<div class="mobile-menu-overlay">
<div class="close-menu-overlay">
</div>
</div>
{% include signup-modal.html %}
<!-- <script src="{{ site.url }}/lib/codemirror/codemirror.js"></script>
<script src="{{ site.url }}/lib/codemirror/mode/javascript/javascript.js"></script>
<script src="{{ site.url }}/lib/codemirror/mode/jsx/jsx.js"></script> -->
<script src="https://code.jquery.com/jquery-2.2.3.min.js" integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo=" crossorigin="anonymous"></script>
<script type="text/javascript" src="{{ site.url }}/js/headroom.min.js"></script>
<script src="{{ site.url }}/js/flourish.js"></script>
<script src="{{ site.url }}/js/main.js"></script>
<script src="{{ site.url }}/js/docs.js"></script>
<script src="{{ site.url }}/js/prism.js" data-manual></script>
{% include ga.html %}
</body>
</html>
================================================
FILE: _sass/animation.scss
================================================
$imagesPath: '../img/';
/* animation container */
.shoutem-ani {
position: absolute;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
.particle {
position: absolute;
pointer-events: none;
user-select: none;
-webkit-touch-callout: none;
touch-callout: none;
}
.particle-1 {
@include size(14px, 14px);
@include background-png-3x(particle-1);
}
.particle-2 {
@include size(24px, 24px);
@include background-png-3x(particle-2);
}
.particle-3 {
@include size(15px, 15px);
@include background-png-3x(particle-3);
/*@include rotating();*/
}
.particle-4 {
@include size(15px, 15px);
@include background-png-3x(particle-4);
/*@include rotating();*/
}
.particle-5 {
@include size(75px, 95px);
@include background-png-3x(particle-5);
.thrust-line {
position: absolute;
top: 77px;
@include size(3px, 15px);
@include border-radius(2px);
background: white;
animation: ani-thrust-line 1.1s linear infinite;
}
.thrust-line-1 {
left: 19px;
}
.thrust-line-2 {
left: 24px;
animation-delay: 0.25s;
}
.thrust-line-3 {
left: 29px;
animation-delay: 0.65s;
}
.thrust-line-4 {
left: 43px;
animation-delay: 0.35s;
}
.thrust-line-5 {
left: 48px;
animation-delay: 0.85s;
}
.thrust-line-6 {
left: 53px;
animation-delay: 0.55s;
}
}
}
================================================
FILE: _sass/base.scss
================================================
/* Bootstrap overrides */
body {
color: $primary;
line-height: 30px;
}
blockquote {
border-width: 1px;
font-size: 17px;
padding: 1px 22px;
}
blockquote p {
color: $primary
}
blockquote .small,
blockquote footer,
blockquote small {
display: block;
font-size: 16px;
font-weight: normal;
}
/*Remove line from blockqute*/
blockquote .small:before,
blockquote footer:before,
blockquote small:before {
content: "";
}
pre {
border-color: $code;
margin-bottom: 24px;
}
hr {
display: none;
}
hr.menu-hr {
display: block;
border-top: 1px solid #ccc;
margin-top: 15px;
margin-bottom: 12px;
}
p {
margin-bottom: 20px;
color: $text;
}
h1 {
@include headline(48px, 48px);
}
h2 {
@include headline(24px, 32px);
}
h4 {
font-weight: bold;
margin-bottom: 25px;
}
h5 {
@include headline(20px, 32px);
font-weight: normal;
}
a:focus,
a:hover {
color: $accent;
text-decoration: underline;
outline: none;
}
.row {
position: relative;
}
::-moz-selection {
color: white;
background: #00aadf;
text-shadow: none;
}
::selection {
color: white;
background: #00aadf;
text-shadow: none;
}
input[type=text], input[type=tel], input[type=email], input[type=submit], textarea {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-appearance: none;
appearance: none;
resize: none;
outline: none;
}
================================================
FILE: _sass/cards.scss
================================================
.list-cards {
display: flex;
justify-content: space-between;
margin-top: 40px;
@include medium {
flex-direction: column;
.card {
margin-left: auto;
margin-right: auto;
margin-top: 48px;
}
}
}
.card {
display: inline-block;
&.rounded {
border-radius: 4px;
.card-image {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
}
&.shadowed {
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1),
0 0 2px 0 rgba(0, 0, 0, 0.1);
transition: box-shadow ease-out 0.2s;
&:hover,
&:active {
box-shadow: 0 19px 38px 0 rgba(0, 0, 0, 0.3),
0 15px 12px 0 rgba(0, 0, 0, 0.2);
}
}
img {
max-width: 100%;
}
.card-content {
.card-title {
margin-top: 16px;
font-size: 15px;
font-weight: normal;
line-height: 24px;
text-transform: none;
color: #444f6c;
@include medium {
margin-top: 0;
line-height: normal;
}
}
p {
color: #888fa1;
font-size: 14px;
line-height: 24px;
@include medium {
margin-top: 4px;
}
}
}
}
================================================
FILE: _sass/colors.scss
================================================
/*Documentation*/
$accent: #00aadf;
$primary: #444f6c;
$primaryhover: #4a5b85;
$secondary: rgba(68, 79, 108, 0.2);
$secondaryhover: rgba(68, 79, 108, 0.3);
$text: #888fa1;
$code: #f3f3f3;
$lightaccent: rgba(0, 170, 223, 0.08);
$lightaccent-2: rgba(0, 170, 223, 0.16);
/*Landing page*/
$hometext: #697289;
$homeheading: #222b35;
$lightbackground: #efefef;
$footercolor: #7b7f8c;
$footerbackground: #232b40;
$muted: #858585;
$green: #6db81d;
$graybackground: #ebebeb;
$bluebackground: #e5f7fc;
$darkbluebackground: #2e3854;
$chapterfooter: #969cac;
================================================
FILE: _sass/documentation-layout.scss
================================================
.container-fluid,
.navbar .container-fluid
{
max-width: 1920px;
}
#wrapper {
width: 100%;
background: #FFFFFF;
}
.documentation-wrapper {
max-width: 1920px;
padding-left: 280px;
margin: 0 auto;
@media screen and (max-width: 991px) {
padding-left: 0;
}
}
#documentation {
max-width: 870px;
width: 75%;
margin: 0 auto;
@include mobile {
width: auto;
margin: 0 20px;
}
}
.image {
margin-top: 20px;
margin-bottom: 40px;
img {
margin: 0 auto;
display: block;
width: 100%;
border: 1px solid #bdc0cb;
border: none;
}
}
.navbar {
margin-bottom: 0;
}
================================================
FILE: _sass/footer.scss
================================================
.footer {
position: fixed;
bottom: 0;
width: 100%;
z-index: -1;
@include small {
position: static;
}
.signup {
text-align: center;
background-color: #00AADF;
padding: 0 24px;
.signup-title {
@include headline(45px, 60px, #fff);
font-weight: 100;
text-align: center;
padding-top: 80px;
margin-bottom: 15px;
@include mobile {
font-size: 36px;
line-height: 46px;
max-width: 240px;
margin: 0 auto 15px;
padding-top: 60px;
}
}
.signup-description {
color: rgba(#ffffff, 0.8);
line-height: 30px;
max-width: 570px;
margin: 0 auto 32px;
@include mobile {
width: 100%;
margin-bottom: 30px;
}
}
.signup-email {
width: 370px;
height: 56px;
border-radius: 0;
background-color: transparent;
padding: 1px 0 0;
margin-bottom: 80px;
margin-right: 8px;
border: none;
border-bottom: 1px solid rgba(#ffffff, 0.7);
color: #FFF;
&::-webkit-input-placeholder {
color: rgba(#ffffff, 0.7);
}
&::-moz-placeholder { /* Firefox 19+ */
color: rgba(#ffffff, 0.7);
opacity: 1;
}
&:-ms-input-placeholder {
color: rgba(#ffffff, 0.7);
}
&:-moz-placeholder {
color: rgba(#ffffff, 0.7);
opacity: 1;
}
@include easeLinear;
&:focus {
color: #ffffff;
border-color: #ffffff;
background: none;
}
@include small {
margin-bottom: 12px;
margin-left: auto;
margin-right: auto;
width: 100%;
max-width: 280px;
display: block;
}
}
.signup-button {
width: 188px;
height: 56px;
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
text-transform: uppercase;
margin-bottom: 40px;
font-weight: bold;
font-size: 14px;
letter-spacing: 1px;
padding: 0;
@include easeLinear;
&:hover,
&:focus,
&:active {
background-color: #f4f4f4;
border-color: #f4f4f4;
}
@include small {
width: 100%;
max-width: 280px;
margin-bottom: 60px;
}
}
}
.bottom-footer {
background-color: $footerbackground;
height: 90px;
color: $footercolor;
padding-top: 22px;
@include small {
height: 326px;
}
.container-fluid {
max-width: 1210px;
}
.footer-logo {
@include logoimage('../img/shoutem_logo_footer.png', 85px, 30px);
opacity: 0.4;
float: left;
margin-top: 9px;
@include easeLinear;
@include small {
float: none;
display: block;
margin-bottom: 24px;
}
@include small {
margin: 8px 0 12px;
}
&:hover,
&:active {
opacity: 1;
}
}
.footer-menu {
float: right;
opacity: 1;
color: $footercolor;
list-style: none;
margin-top: 10px;
margin-left: 0;
padding-left: 0;
@include small {
float: none;
padding: 0;
}
@include small {
margin-top: 0;
}
li {
float: left;
@include small {
float: none;
display: block;
}
&.icon-twitter a,
&.icon-github a {
background-image: url(/img/icon-twitter.svg);
background-repeat: no-repeat;
background-size: 22px;
background-position: center;
text-indent: -100px;
width: 24px;
height: 24px;
overflow: hidden;
display: inline-block;
margin-left: 48px;
margin-top: 3px;
opacity: 0.4;
&:hover,
&:active {
opacity: 1;
}
@include small {
margin-top: 0;
}
}
&.icon-twitter a {
@include small {
margin-left: 24px;
}
}
&.icon-github {
@include small {
&:before {
content: "Follow";
text-transform: uppercase;
font-size: 13px;
letter-spacing: 1px;
display: block;
color: $text;
opacity: 0.5;
margin: 23px 0 17px;
}
}
a {
background-image: url(/img/icon-github.svg);
background-size: 20px;
margin-left: 37px;
@include small {
margin-left: 0;
float: left;
}
}
}
}
a {
color: $text;
@include easeLinear;
font-size: 14px;
padding: 10px 11px;
margin-left: 22px;
&:hover,
&:active {
color: white;
text-decoration: none;
}
@include small {
margin-left: 0;
display: block;
padding: 5px 0;
}
}
}
}
}
================================================
FILE: _sass/github.scss
================================================
/*
Copyright 2014 GitHub Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.pl-c /* comment */ {
color: #969896;
}
.pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */,
.pl-s .pl-v /* string variable */ {
color: #0086b3;
}
.pl-e /* entity */,
.pl-en /* entity.name */ {
color: #795da3;
}
.pl-s .pl-s1 /* string source */,
.pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ {
color: #333;
}
.pl-ent /* entity.name.tag */ {
color: #63a35c;
}
.pl-k /* keyword, storage, storage.type */ {
color: #a71d5d;
}
.pl-pds /* punctuation.definition.string, string.regexp.character-class */,
.pl-s /* string */,
.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
.pl-sr /* string.regexp */,
.pl-sr .pl-cce /* string.regexp constant.character.escape */,
.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */,
.pl-sr .pl-sre /* string.regexp source.ruby.embedded */ {
color: #183691;
}
.pl-v /* variable */ {
color: #ed6a43;
}
.pl-id /* invalid.deprecated */ {
color: #b52a1d;
}
.pl-ii /* invalid.illegal */ {
background-color: #b52a1d;
color: #f8f8f8;
}
.pl-sr .pl-cce /* string.regexp constant.character.escape */ {
color: #63a35c;
font-weight: bold;
}
.pl-ml /* markup.list */ {
color: #693a17;
}
.pl-mh /* markup.heading */,
.pl-mh .pl-en /* markup.heading entity.name */,
.pl-ms /* meta.separator */ {
color: #1d3e81;
font-weight: bold;
}
.pl-mq /* markup.quote */ {
color: #008080;
}
.pl-mi /* markup.italic */ {
color: #333;
font-style: italic;
}
.pl-mb /* markup.bold */ {
color: #333;
font-weight: bold;
}
.pl-md /* markup.deleted, meta.diff.header.from-file */ {
background-color: #ffecec;
color: #bd2c00;
}
.pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
background-color: #eaffea;
color: #55a532;
}
.pl-mdr /* meta.diff.range */ {
color: #795da3;
font-weight: bold;
}
.pl-mo /* meta.output */ {
color: #1d3e81;
}
================================================
FILE: _sass/helpers.scss
================================================
.center-v {
position: relative;
top: 50%;
transform: translateY(-45%);
}
================================================
FILE: _sass/menu-overlay.scss
================================================
@import "variables";
@import "mixins";
.mobile-menu-overlay {
position: fixed;
visibility: hidden;
top: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 1060;
transition: visibility 0.5s;
@include medium-2 {
&.open {
visibility: visible;
.navigation {
transform: translate(0, 0);
transform: translate3d(0, 0, 0);
}
.close-menu-overlay {
opacity: 1;
}
}
}
.navigation {
position: absolute;
right: 0;
padding-top: 24px;
width: 286px;
height: 100%;
overflow: auto;
background: #FFF;
transform: translate(100%, 0);
transform: translate3d(100%, 0, 0);
transition: transform .5s;
ul li a {
padding-left: 40px;
}
@include tiny {
width: 256px;
}
}
.close-menu-overlay {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
background: rgba(68, 79, 108, 0.5);
opacity: 0;
transition: opacity 0.5s
}
}
================================================
FILE: _sass/mixins.scss
================================================
@mixin headline ($size, $lineheight, $color: $primary) {
font-size: $size;
line-height: $lineheight;
color: $color;
}
@mixin logoimage ($imageurl, $width, $height) {
background-image: url($imageurl);
width: $width;
height: $height;
background-size: 100%;
background-repeat: no-repeat;
}
@mixin flexcontainer () {
display: flex;
align-items: center;
justify-content: center;
}
/// Mixin to manage responsive breakpoints
/// @author Hugo Giraudel
/// @param {String} $breakpoint - Breakpoint name
/// @require $breakpoints
@mixin respond-to($breakpoint) {
// If the key exists in the map
@if map-has-key($breakpoints, $breakpoint) {
// Prints a media query based on the value
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
// If the key doesn't exist in the map
@else {
@warn "Unfortunately, no value could be retrieved from `#{$breakpoint}`. "
+ "Available breakpoints are: #{map-keys($breakpoints)}.";
}
}
@mixin tiny {
@media only screen and (max-width: 359px) {
@content;
}
}
@mixin mobile {
@media only screen and (max-width: 480px) {
@content;
}
}
@mixin small {
@media only screen and (max-width: 640px) {
@content;
}
}
@mixin medium {
@media only screen and (max-width: 768px) {
@content;
}
}
@mixin medium-2 {
@media only screen and (max-width: 992px) {
@content;
}
}
@mixin large {
@media only screen and (max-width: 1280px) {
@content;
}
}
@mixin large-2 {
@media only screen and (max-width: 1440px) {
@content;
}
}
@mixin background-png-3x ($image) {
background-repeat: no-repeat;
background-image: url("#{$imagesPath}#{$image}.png");
@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
background-image: url("#{$imagesPath}#{$image}@2x.png");
background-size: 100%;
}
@media (-webkit-min-device-pixel-ratio: 2.5), (min-resolution: 216dpi) {
background-image: url("#{$imagesPath}#{$image}@3x.png");
background-size: 100%;
}
}
@mixin size ($w, $h) {
width: $w;
height: $h;
}
@mixin border-radius ($r) {
-webkit-border-radius: $r;
-moz-border-radius: $r;
-o-border-radius: $r;
-ms-border-radius: $r;
border-radius: $r;
}
@mixin easeLinear ($time: 0.2s, $easing: linear, $props: all) {
transition: $props $time $easing 0s;
}
@mixin rotating ($time: 9s) {
animation-name: ani-rotate;
animation-duration: $time;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes ani-rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes ani-thrust-line {
0% { height: 3px; }
20% { height: 15px; }
100% { height: 3px; }
}
================================================
FILE: _sass/navbar.scss
================================================
.headroom {
will-change: transform;
transition: transform 200ms linear;
}
.headroom--pinned {
transform: translateY(0%);
}
.headroom--unpinned {
transform: translateY(-100%);
}
.navbar-right {
margin-right: 0;
}
.navbar-default .navbar-nav {
font-size: 15px;
.btn {
font-size: 15px;
}
.active {
.navbar-nav--a--link {
color: $accent;
background: none;
transition: none;
&:hover,
&:focus {
background: none;
color: $accent;
}
&::after,
&::before {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
&::before {
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
}
}
.navbar-nav--a--link {
position: relative;
color: $hometext;
@include easeLinear(0.4s, linear, color);
&:hover, &:focus {
color: $accent;
}
&:hover::before,
&:focus::before {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
&::before, &::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 4px;
-webkit-transform: scale3d(0, 1, 1);
transform: scale3d(0, 1, 1);
-webkit-transform-origin: center left;
transform-origin: center left;
}
&::after {
background: $accent;
}
&::before {
-webkit-transition: transform 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
transition: transform 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
background: #00BEF7;
}
}
.active>.navbar-nav--a--link,
.navbar-nav--a--link:focus,
.navbar-nav--a--link:hover {
}
.btn-primary {
margin-top: 18px;
border-radius: 4px;
height: 48px;
padding-top: 12px;
color: #fff;
font-weight: bold;
background: $accent;
@include easeLinear();
&:focus,
&:hover {
color: #fff;
background-color: #00BEF7;
border: 1px solid #00BEF7;
}
}
}
.navbar .container-fluid {
max-width: 1210px;
}
.navbar .navbar-brand {
padding: 0;
height: auto;
margin-top: 20px;
margin-left: 0;
margin-right: 0;
}
.navbar .navbar-brand--logo {
background-image: url('../img/logo-shoutem-for-developers.svg');
background-position: left center;
background-repeat: no-repeat;
background-size: 100%;
width: 215px;
height: 34px;
text-indent: -1000%;
overflow: hidden;
display: block;
}
.home .navbar-brand--logo {
@media (min-width: 768px) {
margin-left: 14px;
}
}
.documentation .navbar-brand {
@include easeLinear();
&:hover {
background: #00AADF;
}
svg {
display: block;
}
#documentation-logo {
@include easeLinear();
}
&:hover #documentation-logo {
fill: #FFF;
}
}
#wrapper {
padding-top: 80px;
@include small {
margin-bottom: 0 !important;
}
}
@media (min-width: 768px) {
.navbar-nav>li>a {
padding: 30px 0 28px;
margin-right: 48px;
}
.navbar-nav>li>a.btn-primary {
padding: 12px 16px 0;
margin-right: 0px;
}
}
.navbar-default .navbar-toggle {
border: none;
padding: 18px 15px;
margin: 16px 0 0;
&:hover {
background: none;
.icon-bar {
background-color: #00AADF;
}
}
}
.navbar-toggle .icon-bar {
width: 17px;
height: 1px;
}
.home .breadcrumb {
display: none;
}
.breadcrumb {
margin-top: 19px;
margin-bottom: 0;
margin-left: 24px;
background: none;
color: $primary;
float: left;
li:first-child {
opacity: 0.7;
}
li + li {
&:before {
content: "";
display: inline-block;
background: url(/img/icon-descriptive-next.svg) center center no-repeat;
width: 24px;
height: 12px;
margin: 0 17px 0 13px;
}
@include small {
display: none;
}
}
@include small {
margin-top: 14px;
}
}
================================================
FILE: _sass/pager.scss
================================================
#pager-wrapper
{
max-width: 1440px;
margin: 80px auto 0;
.pager {
background: #FFFFFF;
margin: 0 0 0 280px;
@include medium-2 {
margin-left: 0;
}
a {
width: 50%;
border: none;
border-radius: 0;
height: 88px;
padding-top: 31px;
@include small {
height: 72px;
padding-top: 28px;
line-height: normal;
}
}
.previous a {
background-color: $lightaccent;
color: $accent;
transition: background 0.2s linear;
&:hover {
background-color: rgba(0,170,223,0.14);
}
&::before {
content: " ";
display: inline-block;
width: 13px;
height: 13px;
margin-right: 24px;
background: url(/img/icon-arrow-left.svg) center center no-repeat;
vertical-align: middle;
margin-bottom: 1px;
}
@include small {
color: transparent;
width: 72px !important;
overflow: hidden;
}
}
.next a {
background-color: $accent;
color: #fff;
transition: background 0.2s linear;
&:hover {
background-color: #00bef7;
}
&::after {
content: " ";
display: inline-block;
background: url(/img/icon-arrow-right.svg) center center no-repeat;
width: 15px;
height: 13px;
vertical-align: middle;
@include small {
height: 72px;
line-height: 72px;
background-size: 15px 13px;
vertical-align: middle;
margin-left: -24px;
}
}
span {
display: inline-block;
vertical-align: middle;
margin-right: 24px;
@include small {
margin-right: 36px;
}
}
@include small {
width: calc(100% - 72px);
padding: 0 10px 0 0;
}
}
.previous {
@include small {
overflow: hidden;
width: 72px !important;
}
}
img {
position: relative;
width: 13px;
height: 13px;
bottom: 1px;
}
.docs-pager-icon-left {
right: 24px;
}
.docs-pager-icon-right {
left: 24px;
}
.inactive
{
background-color: $lightaccent;
float: left;
width: 50%;
height: 88px;
padding-top: 32px;
&.next {
background-color: $accent;
@include small {
width: calc(100% - 72px);
}
}
a {
display: none;
}
@include small {
height: 72px;
}
}
}
}
================================================
FILE: _sass/sidebar.scss
================================================
.navigation {
list-style: none;
margin: 0;
padding: 0;
background: #FFF;
ul {
list-style: none;
padding: 0;
margin: 0;
position: relative;
li a {
padding-left: 40px;
}
&.level-2 {
.menu-group-title {
font-size: 14px;
font-weight: 700;
}
}
&.level-3 {
&::before {
content: "";
width: 1px;
position: absolute;
background-color: #000;
opacity: 0.08;
left: 58px;
top: 11px;
bottom: 11px;
height: initial;
}
}
}
li {
font-size: 14px;
&.primary > a {
height: 48px;
line-height: 48px;
font-size: 15px;
font-weight: 700;
}
.active {
color: $accent;
&:not(.menu-group-title) {
background-color: $lightaccent;
}
}
a {
height: 40px;
width: 100%;
display: inline-block;
line-height: 40px;
vertical-align: middle;
color: $primary;
text-decoration: none;
padding-left: 24px;
font-size: 14px;
@include easeLinear();
&:hover,
&:active,
&:focus {
background-color: $lightaccent-2;
}
}
}
&.level-1 {
> li > a {
font-size: 15px;
font-weight: 700;
height: 48px;
line-height: 48px;
}
}
.sidelink a {
font-weight: 500 !important;
font-size: 14px !important;
height: 40px !important;
line-height: 40px !important;
}
.button-signup {
color: #00aadf;
font-weight: 700;
font-size: 15px;
}
.menu-button-signup {
padding: 15px 0;
border-top: 1px solid #DDD;
border-bottom: 1px solid #DDD;
margin: 15px 0 !important;
}
}
================================================
FILE: _sass/typography.scss
================================================
@font-face {
font-family: 'SourceCode';
src: url('../fonts/sourcecodepro-medium-webfont.eot');
src: url('../fonts/sourcecodepro-medium-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/sourcecodepro-medium-webfont.woff2') format('woff2'),
url('../fonts/sourcecodepro-medium-webfont.woff') format('woff'),
url('../fonts/sourcecodepro-medium-webfont.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'MuseoSans';
src: url('../fonts/museosans-100-webfont.eot');
src: url('../fonts/museosans-100-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/museosans-100-webfont.woff2') format('woff2'),
url('../fonts/museosans-100-webfont.woff') format('woff'),
url('../fonts/museosans-100-webfont.ttf') format('truetype');
font-weight: 100;
font-style: normal;
}
@font-face {
font-family: 'MuseoSans';
src: url('../fonts/museosans-300-webfont.eot');
src: url('../fonts/museosans-300-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/museosans-300-webfont.woff2') format('woff2'),
url('../fonts/museosans-300-webfont.woff') format('woff'),
url('../fonts/museosans-300-webfont.ttf') format('truetype');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'MuseoSans';
src: url('../fonts/museosans_500-webfont.eot');
src: url('../fonts/museosans_500-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/museosans_500-webfont.woff2') format('woff2'),
url('../fonts/museosans_500-webfont.woff') format('woff'),
url('../fonts/museosans_500-webfont.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'MuseoSans';
src: url('../fonts/museosans_700-webfont.eot');
src: url('../fonts/museosans_700-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/museosans_700-webfont.woff2') format('woff2'),
url('../fonts/museosans_700-webfont.woff') format('woff'),
url('../fonts/museosans_700-webfont.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
body {
/* makes font rendering the same on Windows/OSX/iOS/Android */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
================================================
FILE: _sass/variables.scss
================================================
$breakpoints: (
'small': 768px,
'medium': 992px,
'large': 1200px
) !default;
================================================
FILE: css/documentation.scss
================================================
---
---
@import "colors";
@import "mixins";
@import "documentation-layout";
@import "sidebar";
@import "pager";
@import "variables";
@import "footer";
@import "menu-overlay";
@import "cards";
h1{
margin-top: 70px;
margin-bottom: 33px;
@include mobile {
font-size: 36px;
margin-top: 34px;
}
}
h2 {
font-weight: 300;
margin-top: 64px;
margin-bottom: 16px;
@include mobile {
font-size: 18px;
line-height: 32px;
margin-top: 48px;
font-weight: 500;
}
}
h2[id^=api], h2[id^=example], h2[id^=examples] {
margin-top: 8px;
}
h3 {
@include mobile {
font-size: 16px;
line-height: 30px;
font-weight: 500;
}
}
h4 {
font-size: 15px;
}
code {
border-radius: 2px;
}
h3 code, h4 code {
background: inherit;
font-weight: inherit;
color: inherit;
font-size: inherit;
}
.docs-component-image {
max-width: 100%;
margin-bottom: 24px;
}
.docs-codeblock-path {
color: #888fa1;
font-size: 15px;
}
pre code.language-shellsession {
color: #697289 !important;
display: block;
}
pre code.language-shellsession::first-line {
color: #343D59;
}
pre code.language-shellsession .dollar-sign {
color: #697289;
}
h4[id^=jsx-declaration] {
margin-top: -28px;
}
h4[id^=jsx-declaration], h4[id^=style] {
margin-bottom: 16px;
}
h4[id^=jsx-declaration] + pre, h4[id^=style] + pre{
margin-bottom: 44px;
}
h4[id^=props] + ul, h4[id^=style] + ul{
margin-bottom: 44px;
}
code[class*="language-"], pre[class*="language-"] {
font-family: SourceCodePro-Medium, Menlo, Monaco, Consolas,"Courier New", monospace;
text-shadow: none;
color: #444f6c;
line-height: 24px;
}
:not(pre) > code[class*="language-"], pre[class*="language-"] {
background-color: #f3f3f3;
}
pre code {
color: #444f6c;
}
pre {
font-size: 14px;
border: none;
}
pre, pre[class*="language-"] {
margin-bottom: 40px;
}
blockquote p {
color: #444f6c;
font-size: 16px;
line-height: 28px;
}
blockquote h4 + p {
margin-top: -16px;
}
blockquote ol:last-child, blockquote p:last-child, blockquote ul:last-child {
margin-bottom: 9px;
}
blockquote {
border-left: 1px solid #C4C7D0;
}
blockquote h4 {
margin-top: 20px;
}
blockquote p {
margin-top: 13px;
}
blockquote {
font-size: 18px;
padding: 1px 38px;
}
p > a {
border-bottom: 1px solid rgba(0, 170, 223, 0);
transition: border-bottom-color 0.2s linear;
}
p > a:hover {
text-decoration: none;
border-bottom-color: rgba(0, 170, 223, 1);
}
pre[data-line] {
padding-left: 1em;
}
.line-highlight {
background: #00aadf;
opacity: 0.2;
&:before, &[data-end]:after {
content: "";
}
}
#documentation
{
opacity: 1;
transition: opacity 0.2s;
}
.loading #documentation
{
opacity: 0;
}
/* Navbar */
.navbar {
border-bottom: 1px solid #ececec;
@include small {
min-height: 0;
height: 72px;
}
.navbar-header {
@media (min-width: 768px) and (max-width: 991px) {
width: 100%;
}
}
&.navbar-default .navbar-toggle {
margin-right: 20px;
@media (min-width: 768px) {
margin-right: 5px;
}
}
.navbar-brand {
padding: 31px;
margin-top: 0;
border-right: 1px solid #ececec;
@include small {
padding: 27px;
}
}
.navbar-brand--logo {
background-image: url('../img/icon-shoutem-mark.svg');
width: 18px;
height: 18px;
}
.navbar-toggle {
@media (min-width: 768px) {
display: block;
}
@media (min-width: 992px) {
display: none;
}
@include small {
margin-top: 12px;
}
}
.navbar-search-form.navbar-form.navbar-form-border {
margin: 0;
margin-right: 24px;
border-left: 1px solid #ececec;
border-right: 1px solid #ececec;
padding: 24px 16px;
.search-icon {
right: 8px;
width: 24px;
height: 24px;
opacity: 0.5;
vertical-align: middle;
}
}
#navbar-search-input {
background-color:transparent !important;
border:none !important;
box-shadow: none !important;
transition: all .5s;
width: 70px;
}
#navbar-search-input:focus {
width: 165px;
}
/* Highlighted search terms */
.algolia-docsearch-suggestion--highlight {
color: #00aadf;
}
/* Highligted search terms in the main category headers */
.algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight {
background-color: #00aadf;
}
/* Currently selected suggestion */
.ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content {
background-color: rgba(0,170,223,0.16) !important;
}
}
.navbar-collapse {
&.collapse {
@media (min-width: 768px) {
display: none !important;
}
@media (min-width: 992px) {
display: block !important;
}
}
}
#documentationTab {
display: none;
}
/* Navigation */
#sidebar-wrapper
{
position: fixed;
left: 0;
bottom: 0;
height: calc(100% - 81px);
width: 280px;
border-right: 1px solid #ececec;
overflow: auto;
padding: 28px 0;
background: #FFF;
z-index: 1070;
@media screen and (min-width: 1921px) {
left: calc(50% - 950px);
}
@media screen and (max-width: 991px) {
left: auto !important;
right: 0;
transform: translate(100%, 0);
transform: translate3d(100%, 0, 0);
transition: transform 0.5s, visibility 0.5s;
height: 100%;
visibility: hidden;
padding: 0;
&.open {
transform: translate(0, 0);
transform: translate3d(0, 0, 0);
visibility: visible;
}
}
.home-link {
padding: 16px 0;
border-bottom: 1px solid #DDD;
margin: 0 0 15px;
@include small {
padding: 11px 0 12px;
}
}
a {
padding-left: 48px;
}
ul ul a {
padding-left: 56px;
}
ul ul ul a {
padding-left: 74px;
}
.btn-primary {
display: none;
}
}
.menu-group-wrapper
{
display: none;
&.open {
display: block;
}
}
/* Signup: modal version */
#signup {
display: none;
}
#signup-modal
{
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
visibility: hidden;
background-color: rgba(68,79,108,0.5);
z-index: 1080;
transition: opacity 0.35s, visibility 0.35s;
&.open {
opacity: 1;
visibility: visible;
}
.content {
position: absolute;
top: 50%;
left: 50%;
width: 648px;
height: auto;
max-width: calc(100% - 48px);
max-height: calc(100% - 48px);
padding: 55px 48px 0;
background-color: #FFF;
overflow: hidden;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.2);
border-radius: 2px;
transform: scale3d(0.95, 0.95, 0.95) translate3d(-50%, -50%, 0);
transform-origin: left;
transition: transform 0.35s;
@include mobile {
max-width: calc(100% - 40px);
max-height: calc(100% - 40px);
padding: 35px 20px 0;
}
}
&.open .content {
transform: scale3d(1, 1, 1) translate3d(-50%, -50%, 0);
}
.signup-title {
font-size: 24px;
font-weight: 300;
}
.signup-description {
font-weight: 500;
font-size: 14px;
line-height: 24px;
color: #7b8397;
margin: 20px 0 0 0;
}
.signup-email {
height: 48px;
background-color: rgba(228, 230, 233, 0.4);
box-shadow: inset 0 -1px 0 0 rgba(68, 79, 108, 0.3);
border: none;
border-radius: 0;
color: $primary;
font-weight: 500;
&:focus {
box-shadow: inset 0 -2px 0 0 #00aadf;
}
&::-webkit-input-placeholder {
color: #444f6c;
}
&::-moz-placeholder { /* Firefox 19+ */
color: #444f6c;
opacity: 1;
}
&:-ms-input-placeholder {
color: #444f6c;
}
&:-moz-placeholder {
color: #444f6c;
opacity: 1;
}
}
label {
font-size: 13px;
color: $text;
font-weight: 500;
line-height: 1;
margin: 25px 0 0;
}
.control-buttons {
padding: 16px 48px;
margin: 56px -48px 0;
background-color: #f6f7f8;
text-align: right;
@include mobile {
margin-top: 32px;
}
}
.signup-button, .cancel-button {
width: 120px;
height: 40px;
line-height: 38px;
border: 1px solid #fff;
border-radius: 4px;
color: #FFF;
background-color: $primary;
text-transform: uppercase;
font-weight: 700;
font-size: 13px;
letter-spacing: 1px;
padding: 1px 0 0;
@include easeLinear;
@include mobile {
width: auto;
padding: 1px 16px 0;
}
}
.signup-button {
&:hover, &:focus,
&:active {
background-color: $primaryhover;
}
}
.cancel-button {
color: $primary;
background-color: $secondary;
margin-right: 10px;
&:hover, &:focus,
&:active {
background-color: $secondaryhover;
outline: none;
}
}
}
.video-screen {
width: 100%;
background-color: #EEE;
padding: 40px;
border-radius: 4px;
margin-bottom: 40px;
video {
display: block;
width: 280px;
margin: 0 auto;
}
}
.edit-link {
padding: 30px 0 28px;
a {
font-size: 15px;
font-weight: 500;
padding: 0 0 0 32px !important;
display: inline-block;
color: #444f6c;
background: url(../img/icon-github-dark.svg) left center no-repeat;
transition: opacity 0.25s;
&:hover {
opacity: 0.65;
}
}
}
.overview {
h1 {
/*margin-top: 96px;*/
margin-bottom: 0;
line-height: 65px;
}
.section {
width: 100%;
max-width: 870px;
&.section-intro {
margin-top: 48px;
}
&::after {
content: "";
display: table;
clear: both;
}
}
h2 {
font-size: 13px;
font-weight: bold;
line-height: 32px;
letter-spacing: 1px;
color: rgba(#444f6c, 0.7);
text-transform: uppercase;
padding: 27px 0 32px;
border-bottom: 1px solid rgba(#ededed, 0.7);
}
h3 {
line-height: 32px;
font-weight: 300;
margin-top: 48px;
}
p {
font-size: 15px;
font-weight: normal;
line-height: 25px;
color: #888fa1;
}
.intro-video {
float: left;
width: 50%; /* 432/870 = 49.65% */
height: 270px;
background: url(../img/overview/video-bg@2x.jpg) center center no-repeat;
background-size: cover;
position: relative;
&::after {
content: "";
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background: url(../img/overview/icon-play.svg) center center no-repeat;
}
p {
color: #ffffff;
font-size: 14px;
line-height: 20px;
font-weight: bold;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 0 139px 0 24px;
.time {
position: absolute;
right: 24px;
top: 8px;
text-align: right;
font-weight: normal;
width: 70px;
line-height: 24px;
background: url(../img/overview/icon-time-white.svg) 0 center no-repeat;
}
}
}
.intro-text {
float: right;
/*width: 45%;*/ /* 390/870 = 44.82 */
width: 100%;
/*margin-left: 5%;*/
margin-left: 0;
}
.intro-title {
font-size: 16px;
line-height: 32px;
font-weight: bold;
color: #444f6c;
}
.intro-content {
p {
&:first-child {
margin-top: 16px;
}
small {
font-size: 13px;
}
}
img {
display: block;
margin: 32px 0 16px;
}
}
.card {
text-decoration: none;
max-width: 260px;
.card-title {
margin-top: 12px;
}
.card-image {
min-height: 148px;
background: #444f6c;
text-align: center;
img {
margin-top: 24px;
}
&.extensions img {
margin-top: 32px;
margin-left: 18px;
}
&.themes img {
margin-top: 36px;
}
}
.card-content {
padding: 24px;
p:last-child {
margin-bottom: 0;
}
}
.card-category {
font-size: 12px;
font-weight: bold;
color: #888fa1;
text-transform: uppercase;
letter-spacing: 1px;
line-height: initial;
}
.card-time {
@include headline(14px, 24px, $chapterfooter);
@include flexcontainer();
justify-content: initial;
color: #969CAC;
font-size: 14px;
font-weight: normal;
.icon-time {
display: inline-block;
@include logoimage('../img/icon-time.svg', 19px, 19px);
margin-right: 8px;
position: relative;
top: -2px;
}
.difficulty {
padding-left: 19px;
position: relative;
&::before {
content: "";
position: absolute;
width: 4px;
height: 4px;
left: 8px;
top: calc(50% - 3px);
background: #969CAC;
border-radius: 50%;
}
}
}
}
.list-cards.row-2 {
.card {
.card-title {
font-size: 15px;
}
.card-time {
margin-top: 48px;
}
}
}
.logotypes {
margin: 44px 0 0;
padding: 0;
list-style: none;
li {
margin: 0;
padding: 88px 0 0;
width: 25%;
float: left;
font-size: 14px;
font-weight: normal;
line-height: 22px;
background: url(../img/overview/logo-js.svg) 0 0 no-repeat;
&.react-native {
background-image: url(../img/overview/icon-react-native.svg);
}
&.shoutem-ui-toolkit {
background-image: url(../img/overview/icon-ui-toolkit.svg);
}
&.code-editor {
background-image: url(../img/overview/icon-code-editor.svg);
}
}
}
.section-faq {
padding-bottom: 120px;
dt,
dd {
font-size: 15px;
line-height: 32px;
font-weight: 500;
color: #444f6c;
}
dd {
color: #888fa1;
}
}
}
================================================
FILE: css/home.scss
================================================
---
---
@import "colors";
@import "variables";
@import "mixins";
@import "footer";
@import "animation";
@import "helpers";
@import "sidebar";
@import "menu-overlay";
.container, .container-fluid
{
padding-left: 20px;
padding-right: 20px;
}
.row {
margin-left: -20px;
margin-right: -20px;
}
.container-fluid>.navbar-collapse,
.container-fluid>.navbar-header,
.container>.navbar-collapse,
.container>.navbar-header {
margin-left: 0;
margin-right: 0;
}
p {
color: $hometext;
}
.group {
display: inline-block;
}
.section-header {
@include headline(32px, 32px, $homeheading);
font-weight: 300;
text-align: center;
padding-top: 80px;
margin-bottom: 26px;
color: #444f6c;
letter-spacing: -0.5px;
@include mobile {
font-size: 28px;
line-height: 1.45;
padding: 60px 25px 0;
margin-bottom: 24px;
}
}
.section-header--big {
font-weight: 100;
margin-bottom: 60px;
font-size: 48px;
letter-spacing: normal;
@include mobile {
font-size: 36px;
line-height: 46px;
margin-left: auto;
margin-right: auto;
margin-bottom: 0;
padding: 0;
}
}
.section-sub-header {
width: 55%;
margin: 0 auto 32px;
text-align: center;
font-size: 16px;
font-weight: 500;
line-height: 30px;
color: #888fa1;
@include mobile {
width: 100%;
max-width: 280px !important;
}
}
.section-footer {
@include headline(15px, 26px);
color: rgba(#444f6c, 0.7);
text-align: center;
margin-bottom: 30px;
}
.sub-text {
@include headline(14px, 26px);
}
.section {
width: 100%;
max-width: 1170px;
margin: 0 auto;
padding-left: 20px;
padding-right: 20px;
.row {
@include small {
flex-direction: column;
}
}
.bullet-points {
img {
max-width: 100%;
margin: 0 auto;
display: block;
}
@include small {
width: 100%;
margin-top: 33px;
margin-bottom: 54px;
}
}
}
.section.narrow {
max-width: 1024px;
position: relative;
@include small {
padding: 0 15px;
}
}
.section.wider {
max-width: 1172px;
.section-content {
overflow: hidden;
}
}
.flex-container {
max-width: 1000px;
margin: 0 auto 80px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
@include medium {
flex-direction: column;
}
}
.section-get-started {
.flex-container {
max-width: 1030px;
margin-bottom: 60px;
@include mobile {
margin-bottom: 15px;
}
}
.section-header {
padding-top: 90px;
margin-bottom: 70px;
@include mobile {
padding-top: 0;
margin-top: 60px;
margin-bottom: 30px;
}
}
}
.gray-background {
background-color: $graybackground;
}
.blue-background {
background-color: $bluebackground;
}
.dark-blue-background {
background-color: $darkbluebackground;
}
.left-align {
text-align: left;
}
.header {
height: 600px;
background-image: url("../img/shoutem-developers-header.jpg");
background-repeat: no-repeat;
background-position: center;
background-size: cover;
@include mobile {
height: 475px;
}
}
.header-wrapper {
max-width: 1440px;
width: 100%;
margin: 0 auto;
display: flex;
align-items: center;
z-index: 2;
height: 100%;
}
.video-screen {
width: 279px;
height: calc(279px * (650 / 368));
right: 269px;
top: 142px;
z-index: 100;
transform: perspective(1983px) rotateY(-35.5deg) rotateX(11.9deg) rotateZ(16.15deg) scaleY(1.00075);
position: absolute;
backface-visibility: hidden;
transform-style: preserve-3d;
@include large-2 {
right: 98px;
top: 182.5px;
}
@include medium-2 {
display: none;
}
}
.main {
color: #fff; // test!
height: 100%;
&.with-arrow:after {
@media screen and (min-width: 480px) {
bottom: 20px;
}
}
.header-text {
margin: 44px auto 0;
@include medium {
padding: 0;
}
@include mobile {
max-width: 280px;
br {
display: none;
}
}
}
.with-image .header-text {
text-align: left;
margin-left: 5%;
@media (min-width: 1440px) {
margin-left: 235px;
}
@include large-2 {
margin-left: 110px;
max-width: 55%;
}
@include medium-2 {
margin: 20px auto 0;
max-width: 600px;
}
@include mobile {
margin: -22px auto 0;
max-width: 280px;
}
}
.main-heading {
color: #FFFFFF;
font-size: 54px;
line-height: 61px;
font-weight: 100;
margin-top: 0;
margin-bottom: 0;
text-align: center;
max-width: 800px;
@include medium-2 {
font-size: 48px;
}
@include small {
font-size: 36px;
font-weight: 300;
}
@include mobile {
font-size: 24px;
font-weight: 500;
line-height: 38px;
}
}
.with-image .main-heading {
text-align: left;
@include medium-2 {
text-align: center;
}
}
.p, .sub-heading {
color: #EFEFF2;
font-size: 16px;
font-weight: normal;
line-height: 26px;
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
text-align: center;
max-width: 550px;
margin-top: 27px;
}
.with-image .sub-heading {
margin-top: 23px;
text-align: left;
font-size: 16px;
font-weight: 500;
max-width: 380px;
@include large-2 {
br {
display: none;
}
}
@include medium-2 {
text-align: center;
max-width: 360px;
margin-left: auto;
margin-right: auto;
}
@include mobile {
text-align: center;
font-weight: 500;
margin-top: 12px;
}
}
.os-icons {
text-align: center;
margin-top: 27px;
font-size: 0;
img:not(:last-child) {
margin-right: 32px;
}
.icon-android {
position: relative;
margin-top: 3px;
}
}
}
.with-arrow:after {
content: "";
position: absolute;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
background: url("../img/icon-scroll-down.png") no-repeat center / 100%;
display: block;
height: 78px;
width: 33px;
}
.separator {
width: 100%;
height: 1px;
background: rgba(49, 60, 89, 0.1);
margin: 0 auto 21px;
}
.step-label {
display: block;
margin-bottom: 19px;
strong {
font-size: 14px;
border: rgba(49, 60, 89, 0.2) solid 1px;
border-radius: 4px;
padding: 7px 14px 5px;
color: #313c59;
line-height: 1.4;
letter-spacing: 1.2px;
color: #444f6c;
font-weight: 700;
text-transform: uppercase;
position: relative;
top: -6px;
text-align: center;
}
@include medium-2 {
margin: 5px auto 20px;
}
}
.steps {
padding-left: 0;
list-style: none;
}
.steps li {
text-align: center;
min-height: 130px;
.step-title {
font-weight: bold;
text-transform: uppercase;
margin-bottom: 16px;
font-size: 13px;
line-height: 20 px;
letter-spacing: 1.08333px;
color: #444F6C;
@include medium-2 {
margin: 20px 0;
}
@include medium-2 {
margin: 20px 0 15px;
}
}
.step-button {
text-transform: uppercase;
color: #fff;
font-weight: bold;
font-size: 13px;
padding: 0 24px;
height: 90px;
width: 260px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
box-shadow: none;
letter-spacing: 1px;
line-height: 24px;
&.publish-button {
background-color: $green;
border: 1px solid $green;
border-radius: 4px;
position: relative;
@include medium-2 {
height: 70px;
}
}
span {
display: block;
}
@include medium-2 {
margin-right: 0;
left: 0;
border-radius: 4px;
width: 280px;
height: 75px;
}
}
.arrow-box {
position: relative;
background: $accent;
border: 1px solid $accent;
padding: 0 16px 0 24px;
&:after,
&:before {
left: 100%;
top: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
@include medium-2 {
border-top-color: #00aadf;
border-left-color: transparent !important;
border-width: 138px !important;
border-radius: 4px;
transform: scaleY(0.1);
left: 0;
bottom: -153px;
top: auto;
margin-top: 0 !important;
}
}
&:after {
border-left-color: $accent;
border-width: 44px;
margin-top: -44px;
}
&:before {
border-left-color: $accent;
border-width: 45px;
margin-top: -45px;
margin-left: 1px;
@include medium-2 {
left: 1px;
bottom: -152px;
}
}
@include medium-2 {
margin-bottom: 20px;
}
}
}
.section-step0 {
padding-bottom: 100px;
.section-header {
padding-top: 90px;
@include small {
padding-top: 40px;
}
}
.section-footer {
width: 60%;
margin: 0 auto;
font-weight: normal;
@include small {
max-width: 280px;
width: 100%;
line-height: 26px;
}
}
.flex-container {
background: linear-gradient(to bottom, transparent 45px, rgba(0, 170, 223, 0.1) 45px);
max-width: 970px;
margin-bottom: 48px;
@include medium-2 {
flex-direction: column;
overflow: hidden;
background: none;
}
@include small {
margin-bottom: 42px;
background: none;
}
}
&.with-arrow:after {
height: 88px;
background-image: url("../img/image-arrow-blue.svg");
background-size: contain;
bottom: -32px;
z-index: 3;
@include small {
bottom: -44px;
}
}
@include small {
padding-bottom: 48px;
}
}
.section-customization {
background-color: white;
padding-bottom: 100px;
.section-header {
padding-top: 82px;
@include mobile {
font-size: 24px;
}
}
.separator {
margin-bottom: 8px;
width: 299px;
@include medium {
max-width: 100%;
}
}
.builder-image {
align-self: flex-end;
margin-right: 0;
img {
max-width: 100%;
@include small {
width: 100%;
}
@include mobile {
max-width: 335px;
}
}
@include medium-2 {
width: calc(100% - 360px);
}
@include small {
width: 100%;
padding: 0;
text-align: center;
position: relative;
&:before {
content: " ";
height: 1px;
width: calc(100% - 40px);
max-width: 335px;
background: #000;
opacity: 0.1;
position: absolute;
bottom: 0;
z-index: 1050;
}
}
}
.row {
display: flex;
align-items: center;
margin-left: -65px;
margin-bottom: -30px;
@include small {
margin: 0;
}
}
.bullet-points {
padding: 0;
margin: -30px 0 0 8.3333333333%;
img {
max-width: 100%;
margin: 0 auto;
display: block;
}
.integrations {
padding-top: 5px;
@include medium-2 {
img {
margin: 0;
}
}
}
li:not(.integrations) {
margin-right: 0;
}
@include large {
margin-left: 45px;
}
@media (max-width: 1120px) {
margin-left: 15px;
}
@include large {
width: 420px;
margin-top: 0;
}
@include small {
width: 100%;
margin-top: 33px;
margin-bottom: 54px;
margin-left: 0;
}
}
&.with-arrow:after {
height: 88px;
background-image: url("../img/image-arrow-blue.svg");
background-size: contain;
bottom: -5px;
margin-left: -225px;
z-index: 3;
@include small {
bottom: -44px;
margin-left: 0;
}
}
.dragdealer {
.handle {
cursor: move;
cursor: -webkit-grab;
cursor: grab;
transition: transform 0.5s ease-out;
}
&.active .handle {
cursor: move;
cursor: -webkit-grabbing;
cursor: grabbing;
}
}
.screen-type-cards,
.visual-style-cards,
.layout-cards {
display: block;
position: relative;
max-width: 1170px;
padding-bottom: 3px;
padding-left: 0;
padding-right: 0;
overflow: hidden;
border-radius: 4px;
margin-top: 60px;
.subtitle {
font-size: 18px;
font-weight: normal;
line-height: 26px;
text-align: center;
margin: 0 0 32px;
}
&::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 13px;
height: 100%;
background: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.05));
pointer-events: none;
}
.card {
float: left;
margin: 0 0 20px;
width: 190px;
text-align: left;
.inner {
margin: 0;
width: 190px;
height: 214px;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 0 2px 0 rgba(0,0,0,0.03),
0 2px 2px 0 rgba(0,0,0,0.06);
}
&:not(:last-child) {
width: 214px;
.inner {
margin-right: 24px;
}
}
img {
display: block;
max-width: 100%;
}
.card-title {
margin: 16px 0 0;
padding: 0 16px;
font-size: 16px;
line-height: 20px;
font-weight: normal;
text-transform: none;
color: $primary;
letter-spacing: 0;
}
p {
font-size: 14px;
margin: 3px 0 0;
padding: 0 16px;
line-height: 22px
;
height: 3em;
overflow: hidden;
}
}
}
.visual-style-cards {
margin-top: 60px;
.card {
width: 154px;
.inner {
width: auto;
height: auto;
padding: 0;
background-color: transparent;
margin: 0;
border-radius: 2px;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.12);
}
&:not(:last-child) {
width: 186px;
.inner {
margin-right: 32px;
}
}
}
}
}
.section-step2 {
.row {
display: flex;
align-items: center;
@include small {
margin: 0;
}
}
.section-header {
padding-top: 89px;
margin-bottom: 107px;
@include medium-2 {
margin-bottom: 48px;
padding-top: 48px;
}
}
.wider {
max-width: 1184px;
}
.image-rn-code {
max-width: 642px;
margin-left: 10px;
margin-bottom: 100px;
padding: 0;
&.with-arrow:after {
height: 88px;
background-image: url("../img/image-arrow-blue.svg");
background-size: contain;
bottom: -83px;
z-index: 3;
@include small {
bottom: -335px;
}
}
img {
max-width: 100%;
}
@include medium-2 {
width: calc(100% - 420px);
margin-bottom: 48px;
}
@include small {
order: -1;
margin-left: -20px;
margin-right: -20px;
width: 100%;
}
}
.builder-screenshot {
@include logoimage('../img/image-builder-screenshot@2x.png', 669px, 372px);
@include medium {
display: none;
}
}
.bullet-points {
margin: -106px 0 0 78px;
.bullet-icon {
margin-right: 20px;
}
@include medium-2 {
width: 360px;
margin-top: 0;
}
@include small {
width: 100%;
margin: 0 0 50px;
}
}
}
.section-step3 {
background-color: #313C59;
min-height: 636px;
color: #ffffff;
.col-md-6 {
@include small {
width: 100%;
margin-left: 0;
}
}
@include small {
min-height: 1165px;
text-align: center;
}
.step-label {
strong {
color: #7ed321;
border: solid 1px #7ed321;
}
@include small {
margin: 10px auto 15px;
}
}
.bullets-wrapper {
display: block;
text-align: left;
.bullet-points {
margin-left: auto;
margin-right: -3px;
max-width: 380px;
width: 100%;
.bullet {
h4 {
color: #ffffff;
font-size: 22px;
line-height: 26px;
font-weight: normal;
margin: 0;
}
p {
margin: 8px 0 0;
max-width: 260px;
font-size: 15px;
line-height: 24px;
color: rgba(#BDC0CB, 0.7);
}
&:not(:first-child) {
h4 {
margin-top: 40px;
}
}
@include medium-2 {
padding-left: 106px;
}
@include mobile {
padding-left: 72px;
}
&.bullet-publish {
background: url(../img/publish.svg) 100% 6px no-repeat;
@include medium-2 {
background-position: 0% 6px;
}
@include mobile {
background-size: 45px;
}
}
&.bullet-manage {
background: url(../img/manage.svg) calc(100% - 19px) 12px no-repeat;
@include medium-2 {
background-position: 0% 6px;
}
@include mobile {
background-size: 45px;
}
}
&.bullet-analyse {
background: url(../img/analyse.svg) calc(100% - 14px) 25px no-repeat;
@include medium-2 {
background-position: 0% 25px;
}
@include mobile {
background-size: 45px;
}
}
}
@include medium-2 {
margin: auto;
}
}
}
.section-header {
padding-top: 75px;
text-align: center;
margin-bottom: 0;
color: #F2F2F5;
@include small {
padding-top: 40px;
margin-bottom: 20px;
text-align: center;
}
}
.section-sub-header {
width: 100%;
margin-top: 20px;
margin-bottom: 0;
text-align: center;
@include small {
text-align: center;
margin: 0 auto 30px;
max-width: 450px;
}
}
.col {
margin-top: 52px;
&.text {
margin-top: 147px;
margin-bottom: 191px;
@include medium-2 {
margin-bottom: 64px;
margin-top: 48px;
}
}
&.carousel {
background: url(../img/mbp-bezel.svg) left top no-repeat;
height: 580px;
overflow: hidden;
@include medium-2 {
background-position: calc(50% + 3px) 0;
padding: 0;
margin-top: 0;
}
@include small {
background-position: calc(50% + 2px) 0;
background-size: 641px auto;
}
.images {
position: relative;
top: 57px;
left: 66px;
@include medium-2 {
text-align: center;
left: 0;
margin-left: auto;
margin-right: auto;
}
@include small {
width: 521px;
top: 40px;
img {
max-width: 100%;
}
}
}
}
}
p {
font-size: 18px;
}
.group {
@include small {
display: block;
margin-left: 30px;
margin-top: 10px;
}
}
.section.fullwidth {
max-width: 100%;
padding: 0 0 0 46px;
@include medium-2 {
padding: 0;
}
}
}
.bullet-points {
margin: 0;
padding: 0;
list-style: none;
}
.bullet-points li {
line-height: 1.5;
margin-bottom: 23px;
padding: 0;
}
.bullet-icon {
display: inline-block;
margin-right: 20px;
vertical-align: middle;
width: 24px;
height: 24px;
padding: 4px;
background-position: 0 0;
@include logoimage('../img/icon-bullet-circle.svg', 24px, 24px);
&.react {
@include logoimage('../img/icon-react-logo.svg', 30px, 30px);
background-position: center;
margin: -2px 0 0 8px !important;
}
}
.bullet-text {
vertical-align: top;
font-size: 15px;
line-height: 24px;
width: calc(100% - 44px);
max-width: 245px;
display: inline-block;
&.react-native {
max-width: 295px;
a {
display: inline-block;
}
}
@include mobile {
font-size: 15px;
line-height: 24px;
}
}
.arrow {
@include logoimage('../img/image-arrow-blue.svg', 28px, 88px);
position: absolute;
bottom: -40px;
z-index: 1;
&.first,
&.second {
left: 50%;
}
&.third {
left: 20%;
}
}
.app-store-logo {
position: relative;
bottom: 2px;
padding: 0 12px;
}
.section-step4 {
background: #ffffff;
padding-bottom: 100px;
.section-header {
padding-top: 90px;
margin-bottom: 23px;
font-size: 45px;
@include medium-2 {
font-size: 36px;
padding-top: 48px;
}
}
}
.section-featured
{
.featured-apps {
text-align: center;
margin-top: 64px;
a {
width: 145px;
height: auto;
display: inline-block;
vertical-align: top;
&:not(:last-child) {
margin-right: 48px;
}
img {
width: 100%;
max-width: 85px;
}
p {
font-size: 14px;
font-weight: normal;
line-height: 20px;
color: #222B35;
margin-top: 26px;
}
}
}
p {
text-align: center;
font-size: 15px;
font-weight: normal;
color: #888fa1;
line-height: 25px;
max-width: 380px;
margin: 0 auto;
}
}
.chapter {
width: 310px;
min-height: 400px;
background-color: #fff;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 0 2px 0 rgba(0, 0, 0, 0.1);
transition: box-shadow ease-out 0.2s;
cursor: pointer;
margin-bottom: 20px;
overflow: hidden;
@include border-radius(4px);
@include medium-2 {
width: 270px;
}
/* just until Elvis figures something better :) */
@media only screen and (max-width: 882px) {
width: 240px;
}
@include medium {
width: 100%;
max-width: 480px;
}
@include mobile {
width: calc(100% - 18px);
max-width: 280px;
margin-bottom: 40px;
}
&:hover,
&:active {
box-shadow: 0 19px 38px 0 rgba(0, 0, 0, 0.3), 0 15px 12px 0 rgba(0, 0, 0, 0.2);
text-decoration: none;
}
&:last-child .chapter-content {
padding-right: 15px;
}
.chapter-content {
padding: 24px;
.chapter-title {
line-height: 140%;
margin-bottom: 10px;
}
.chapter-description {
font-size: 15px;
line-height: 24px;
}
.chapter-time {
@include headline(14px, 24px, $chapterfooter);
@include flexcontainer();
justify-content: initial;
.time-icon {
display: inline-block;
@include logoimage('../img/icon-time.svg', 19px, 19px);
margin-right: 8px;
}
}
}
.chapter-image {
width: 100%;
height: 180px;
@include flexcontainer();
&.blue {
background-color: #58c6fb;
}
&.orange {
background-color: #fe9827;
}
&.purple {
background-color: #bd10e0;
}
&.darkblue {
background-color: #0f75fb;
}
&.greyblue {
background-color: #444f6c;
}
.getting-started-icon {
@include logoimage('../img/icon-tutorial-getting-started.svg', 75px, 93px);
}
.restaurant-app-icon {
@include logoimage('../img/icon-tutorial-restaurant-app.svg', 80px, 82px);
}
.shopping-app-icon {
@include logoimage('../img/icon-tutorial-shopping-app.svg', 104px, 58px);
margin-left: -50px;
}
.loyalty-app-icon {
@include logoimage('../img/icon-tutorial-loyalty-app.svg', 104px, 51px);
margin-left: -30px;
}
.ui-toolkit-icon {
@include logoimage('../img/icon-tutorial-ui-toolkit.svg', 47px, 77px);
}
.shoutem-icon {
@include logoimage('../img/icon-tutorial-shoutem.svg', 113px, 35px);
}
}
}
.documentation-category,
.chapter-category {
font-size: 12px;
font-weight: bold;
color: $muted;
letter-spacing: 1px;
text-transform: uppercase;
line-height: 1;
margin-top: 0;
}
.documentation-group {
width: 270px;
margin-bottom: 80px;
@include medium {
margin: 0 auto 30px;
}
.documentation-link {
position: relative;
padding-left: 40px;
color: $homeheading;
line-height: 30px;
display: block;
padding-top: 20px;
cursor: hover;
&:hover {
color: $homeheading;
text-decoration: none;
}
.link-icon {
@include logoimage('../img/icon-reference.svg', 24px, 30px);
position: absolute;
left: 0;
opacity: 0.6;
}
}
}
.feature {
width: 270px;
min-height: 230px;
@include medium {
margin: 0 auto 30px;
}
.feature-title {
font-size: 17px;
opacity: 0.8;
margin-bottom: 10px;
}
.feature-icon {
margin-bottom: 30px;
&.extensions {
@include logoimage('../img/icon-features-extensions.svg', 24px, 24px);
}
&.styling {
@include logoimage('../img/icon-features-styling.svg', 24px, 24px);
}
&.command-line {
@include logoimage('../img/icon-features-command-line.svg', 9px, 16px);
}
&.analytics {
@include logoimage('../img/icon-features-analytics.svg', 24px, 24px);
}
&.push-notifications {
@include logoimage('../img/icon-features-push-notifications.svg', 24px, 24px);
}
&.monetization {
@include logoimage('../img/icon-features-monetization.svg', 14px, 24px);
}
&.extendable {
@include logoimage('../img/icon-features-extendable.svg', 24px, 24px);
}
&.app-configuration {
@include logoimage('../img/icon-features-app-configuration.svg', 16px, 24px);
}
&.auto-updated {
@include logoimage('../img/icon-features-auto-updated.svg', 24px, 24px);
}
&.scalable {
@include logoimage('../img/icon-features-scalable.svg', 24px, 24px);
}
&.cloud-storage {
@include logoimage('../img/icon-features-cloud-storage.svg', 28px, 20px);
}
&.rest-json-api {
@include logoimage('../img/icon-features-rest-json-api.svg', 24px, 24px);
}
}
}
.app {
width: 170px;
min-height: 120px;
text-align: center;
.app-icon {
height: 85px;
width: 85px;
background-repeat: no-repeat;
background-size: 100%;
margin: 0 auto 25px;
&.leadership {
background-image: url('../img/image-app-leadership-conference.png');
}
&.jax-xplrr {
background-image: url('../img/image-app-jax-xplrr.svg');
}
&.melbourne-central {
background-image: url('../img/image-app-melbourne-central.png');
}
&.firmsconsulting {
background-image: url('../img/image-app-firmsconsulting.svg');
}
&.seln {
background-image: url('../img/image-app-seln-social-network.svg');
}
&.laughing-place {
background-image: url('../img/image-app-laughing-place.png');
}
}
}
.company {
width: 170px;
height: 80px;
@include flexcontainer();
.company-logo {
&.usa-today {
@include logoimage('../img/logo-usa-today.svg', 100px, 53px);
}
&.t-mobile {
@include logoimage('../img/logo-t-mobile.svg', 129px, 23px);
}
&.tnw {
@include logoimage('../img/logo-tnw.svg', 68px, 31px);
}
&.techcrunch {
@include logoimage('../img/logo-techcrunch.svg', 154px, 23px);
}
&.mashable {
@include logoimage('../img/logo-mashable.svg', 126px, 23px);
}
&.entrepreneur {
@include logoimage('../img/logo-entrepreneur.svg', 144px, 29px);
}
}
}
================================================
FILE: css/prism.css
================================================
/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+bash+json+jsx&plugins=line-highlight */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
pre[data-line] {
position: relative;
padding: 1em 0 1em 3em;
}
.line-highlight {
position: absolute;
left: 0;
right: 0;
padding: inherit 0;
margin-top: 1em; /* Same as .prism’s padding-top */
background: hsla(24, 20%, 50%,.08);
background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
pointer-events: none;
line-height: inherit;
white-space: pre;
}
.line-highlight:before,
.line-highlight[data-end]:after {
content: attr(data-start);
position: absolute;
top: .4em;
left: .6em;
min-width: 1em;
padding: 0 .5em;
background-color: hsla(24, 20%, 50%,.4);
color: hsl(24, 20%, 95%);
font: bold 65%/1.5 sans-serif;
text-align: center;
vertical-align: .3em;
border-radius: 999px;
text-shadow: none;
box-shadow: 0 1px white;
}
.line-highlight[data-end]:after {
content: attr(data-end);
top: auto;
bottom: .4em;
}
================================================
FILE: css/style.scss
================================================
---
---
@import "colors";
@import "mixins";
@import "base";
@import "navbar";
@import "typography";
@import "github";
================================================
FILE: csv/restaurants.csv
================================================
Name,Address,Description,Website,Image,E-mail
"Gaspar Brasserie","185 Sutter St, San Francisco, CA 94109","Expect an intimate venue with the ambience of a private club. The mood is casual, the guests sublime.","gasparbrasserie.com","http://shoutem.github.io/static/getting-started/restaurant-1.jpg","info@gasparbrasserie.com"
"Chalk Point Kitchen","527 Broome St, New York, NY 10013","Stylish restaurant serving market-to-table American fare in modern farmhouse digs with cellar bar.","http://www.chalkpointkitchen.com/","http://shoutem.github.io/static/getting-started/restaurant-2.jpg","feedme@chalkpointkitchen.com"
"Kyoto Amber Upper East","225 Mulberry St, New York, NY 10012","Amber Upper East is located on the corner of 80th and 3rd Avenue. We serve Japanese and Asian cuisines.","https://www.opentable.com/amber-upper-east","http://shoutem.github.io/static/getting-started/restaurant-3.jpg","-"
"Sushi Academy","1900 Warner Ave. Unit A Santa Ana, CA","The educational philosophy of the California Sushi Academy crosses borders, race and gender to bring greater Japanese cultural understanding and appreciation through the culinary experience.","http://www.sushi-academy.com/","http://shoutem.github.io/static/getting-started/restaurant-4.jpg","email@sushi-academy.com"
"Sushibo","35 Sipes Key, New York, NY 10012","We are a catalan family company. From the beginning, on September 2010, Sushibo has been the operating brand of Harakirifish SL in Barcelona. Our passion for authentic, fresh Japanese cooking has brought us to connect with an extensive base of clients, as well as hotels and several businesses all around the city.","http://www.sushibo.cat/en/","http://shoutem.github.io/static/getting-started/restaurant-5.jpg","info@sushibo.cat"
"Mastergrill","550 Upton Rue, San Francisco, CA 94109","Master Grill by Marko is a longtime brand owned by Mark Stojkanovic, a grill specialist trained in Leskovac, on a notable international school of top grill.","http://www.master-grill.com/master-grill.html","http://shoutem.github.io/static/getting-started/restaurant-6.jpg","-"
================================================
FILE: docs/cloud/_posts/1970-01-01-ShoutemCloud.md
================================================
---
layout: doc
permalink: /docs/cloud/introduction
title: Introduction
section: Shoutem Cloud
---
# Shoutem Cloud
Shoutem Extensions can be used with any server. For easier integration with backend, we've prepared a library called [@shoutem/redux-io](https://github.com/shoutem/redux-io), a layer on top of [redux](http://redux.js.org/docs/introduction/) which makes it easy to manage data fetching lifecycle.
If you don't have a server to connect with, you can use Shoutem Cloud. Shoutem Cloud is one of the main components of the Shoutem platform. Using Shoutem Cloud, you don't need to worry about developing your own backend with all the problems that come along: optimisations for typical CRUD operations, scaling and security.
[//]: # (Add picture of Shoutem Cloud)
To use Shoutem Cloud with extensions, we need to create [data schemas]({{ site.url }}/docs/extensions/my-first-extension/using-cloud-storage) which describe structure of the data that should be stored on Shoutem Cloud. When used in extensions, the `@shoutem/redux-io` library is by default configured to Shoutem Cloud, but it can be configured for use with any API.
We've also prepared a [data schema reference]({{ site.url }}/docs/cloud/data-schemas). You can see an implementation of how a custom extension uses Shoutem Cloud storage in our [My First Extension]({{ site.url }}/docs/extensions/my-first-extension/introduction) tutorial in the Using Cloud Storage and Working with Data sections. However, we recommend going through the entire tutorial as a whole.
================================================
FILE: docs/cloud/_posts/1970-01-02-DataSchemas.md
================================================
---
layout: doc
permalink: /docs/cloud/data-schemas
title: Introduction
section: Data Schemas
---
# Data Schemas
Data Schema describes what the data will look like. Schemas are a `type` of data. The format of the data schema is nothing more than a Shoutem flavored [JSON schema](https://spacetelescope.github.io/understanding-json-schema/UnderstandingJSONSchema.pdf).
## Usage in extensions
Create a data schema with:
```ShellSession
$ shoutem schema add <schema-name>
```
where you should replace `<schema-name>` with the name of your Data Schema name. Example:
```ShellSession
$ shoutem schema add Restaurants
Schema `Restaurants` is created in file `server/schemas/Restaurants.json`!
File `extension.json` was modified.
```
Data schema is created in the `server/data-schemas` folder. Its default content is:
```JSON
{
"title": "Restaurants",
"properties": {
"name": {
"format": "single-line",
"title": "Name",
"type": "string",
"displayPriority": 1
}
},
"titleProperty": "name",
"type": "object"
}
```
Root JSON fields that are immediately included are:
- `title`: Title of the schema shown on the CMS page
- `properties`: Properties of each object created from that data schema
- `titleProperty`: Property used as title of object in the list
- `type`: Type of data in JavaScript. It can only be `object`
Field `properties` is an object containing _keys_ as names of object properties and _values_ as descriptors of property's value. Shoutem flavored `properties` can't have children. Below is the reference for value descriptor.
## Value descriptor reference
With the value descriptor Shoutem builder knows which input fields to render on the CMS page. These input fields along with the `title` property explain to the application owner which kind of data they expect.
### Value types
Each value type has a combination of `type`, `format` and sometimes additional properties in the value descriptor which define type of the value. We call them _value signatures_. Referencing other data schemas is enabled by using `referencedSchema` field. Below are the signatures and examples for each value type that can be created with data schemas.
> #### Note
> JSON schema defines the types that can be used. It also provides some built-in formats. However, Shoutem uses its own flavored formats.
#### Single-line string
Signature:
```JSON
"type": "string",
"format": "single-line"
```
Example:
```JSON
"name": {
"type": "string",
"format": "single-line",
"title": "Name",
"required": true
}
```
#### Multi-line string
Signature:
```JSON
"type": "string",
"format": "multi-line"
```
Example:
```JSON
"description": {
"type": "string",
"format": "multi-line",
"title": "Description",
"minLength": 10,
"maxLength": 1000
},
```
#### Integer
Signature:
```JSON
"type": "integer",
"format": "integer"
```
Example:
```JSON
"rating": {
"type": "number",
"format": "number",
"title": "Rating",
"minimum": 0,
"maximum": 10
}
```
#### Boolean
Signature:
```JSON
"type": "boolean",
"format": "boolean"
```
Example:
```JSON
"offersWifi": {
"type": "boolean",
"format": "boolean",
"title": "Offers WIFI"
}
```
#### Array
Signature:
```JSON
"type": "array",
"format": "array"
```
Example:
```JSON
"genericArray": {
"type": "array",
"format": "array",
"title": "Generic JS Array"
},
```
#### Generic object
Signature:
```JSON
"type": "object",
"format": "object"
```
Example:
```JSON
"genericObject": {
"type": "object",
"format": "object",
"title": "Generic JS Object"
}
```
#### Date time
Signature:
```JSON
"type": "object",
"format": "date-time"
```
Example:
```JSON
"openedSince": {
"type": "object",
"format": "date-time",
"title": "Opened Since"
}
```
#### Location
Signature:
```JSON
"type": "object",
"format": "geolocation"
```
Example:
```JSON
"placeOfBirth": {
"type": "object",
"format": "geolocation",
"title": "Place of birth"
}
```
#### Image
Signature:
```JSON
"type": "object",
"format": "attachment",
"referencedSchema": "shoutem.core.image-attachments"
```
Example:
```JSON
"image": {
"type": "object",
"format": "attachment",
"title": "Restaurant's image",
"referencedSchema": "shoutem.core.image-attachments"
}
```
#### Rich media
Signature:
```JSON
"type": "string",
"format": "html"
```
Example:
```
"info": {
"type": "string",
"format": "html",
"title": "Info",
"maxLength": 10000
}
```
#### Custom referenced schema - single object
Signature:
```JSON
"type": "object",
"format": "entity-reference",
"referencedSchema": "<<absolute-data-schema-name>>"
```
Example:
```JSON
"Restaurant": {
"type": "object",
"format": "entity-reference",
"title": "Best restaurant",
"referencedSchema": "shoutem.restaurants.Restaurants"
}
```
> #### Note
> The absolute data schema reference is formated following structure {developerName}.{extensionName}.{extensionPartName} as explained in [Creating shortcut and screen]({{ site.url }}/docs/extensions/my-first-extension/shortcut-and-screen) tutorial
#### Custom referenced schema - array
Signature:
```JSON
"type": "object",
"format": "entity-reference-array",
"referencedSchema": "<<absolute-data-schema-name>>"
```
Example:
```JSON
"News": {
"type": "object",
"format": "entity-reference-array",
"title": "News",
"referencedSchema": "shoutem.news.News"
}
```
### Property order
As `properties` are a dictionary and dictionaries are by nature unordered, we added a `displayPriority` property in the value descriptor which you can use to define the order in which properties are shown on the Shoutem CMS interface. A valid value of `displayPriority` property is an integer - the lower the integer, the higher the property will be shown in the interface.
Display priority is an optional property. Properties which omit it will be rendered in an arbitrary order after all the properties with `displayPriority` defined.
### Additional descriptor properties
The value descriptor along with _value type_ can also describe additional information for a particular value. These fields are inherited from the JSON Schema specification:
- `properties.pattern` - regex pattern constraint, applicable only to `string` primitive type
- `properties.required` - required constraint
- `properties.minLength` and `properties.maxLength` - `string` length constraints, applicable only to `string` primitive type
- `properties.maximum` and `properties.minimum` - value range constraints, applicable only to number and integer primitive types
These additional descriptor properties allow us to create arbitrary types, such as generally used e-mail:
```JSON
"email": {
"type": "string",
"format": "single-line",
"title": "Email",
"minLength": 3,
"maxLength": 100,
"pattern": "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
},
```
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-01-Introduction.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/introduction
title: Introduction
section: My First Extension
---
# My First Extension
<hr />
This tutorial will show you how to write custom **Shoutem extensions** on the Shoutem platform. It introduces the most important concepts. After completing it, you will have a running **mobile app** that uses the your brand new **custom extension** with components from the [Shoutem UI Toolkit]({{ site.url }}/docs/ui-toolkit/introduction) and retrieves content from the [Shoutem Cloud]({{ site.url }}/docs/cloud/introduction). The final result of this tutorial-made extension is [open sourced](https://github.com/shoutem/extension-examples/tree/master/restaurants-getting-started).
Before you start the My First Extension tutorial series, you should go through [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started), because this tutorial series builds on top of the simple extension you created there.
Here's a preview of what the completed app will look like.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/extension-preview.jpg'/>
</p>
## What are Extensions?
Extensions represent features in the app. The app owner picks extensions that he wants to use in his app through the Shoutem Builder. Shoutem prepared a bunch of [open sourced](https://github.com/shoutem/extensions) extensions which you can easily customize to fit your needs.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/shoutem-extensions.png'/>
</p>
You can develop extensions both within apps made by `shoutem clone` and as stand-alone extensions that you plan on using within multiple apps. In this tutorial, we'll be working on the Restaurants extension we made in Getting Started, so it'll be treated as one within a _cloned_ app.
## About the Technology
<hr />
Shoutem uses [React](https://facebook.github.io/react/) and [React Native](https://facebook.github.io/react-native/) as frameworks for building cross-platform apps. React is an open source JavaScript library that provides a way to build user interfaces (UIs), while React Native exposes iOS and Android **native** components so they can be used in React environment. If you haven't used these technologies before, our [React Native school](http://school.shoutem.com/) can help you get started!
We use [JSX](https://facebook.github.io/react/docs/introducing-jsx.html) as a simple way to write UI components with tags. Building an app screen with JSX is as easy as:
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/jsx-component-example.png'/>
</p>
On top of React and React Native, we're using [Redux](http://redux.js.org/), a library which simplifies state management.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/redux.png'/>
</p>
Even though _we_ use Redux, _you_ can use any other state management library (like [MobX](https://github.com/mobxjs/mobx), or just don't use any at all! Again, we don't want to restrict you on how you use React Native.
## Create an Extension
The best way to understand the power of extensions is to get your hands dirty. Let's write some code!
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-03-InitializingExtension.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/initializing-extension
title: Creating an Extension
section: My first extension
---
# Creating an Extension
Here's the mockup of the Restaurants extension that we saw in the [Introduction]({{ site.url }}/docs/extensions/my-first-extension/introduction). Through the course of this tutorial, we'll turn the **Restaurants** extension from [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started) into this list of restaurants.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/extension-preview.jpg'/>
</p>
The left app screen lists the restaurants and the right one shows the details of each specific restaurant when you tap on it.
## Initialization
Let's revise what we did in [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started) (which you should go through before starting this tutorial series). We cloned the app we made on the Builder and initialized a new extension in the app with basic information using `shoutem init`, which created a folder and bootstrapped it with extension files.
```ShellSession
$ shoutem init restaurants
Enter information about your extension. Press `return` to accept (default) values.
Title: Restaurants
Version: 0.0.1
Description: A restaurants extension.
```
This information is stored in the `extension.json` file.
> #### Note
> In case you can't remember the structure of some command, type `shoutem -h` or `shoutem <command> -h` where you should replace `<command>` with one of the [CLI commands]({{ site.url }}/docs/extensions/reference/cli).
## Folder Structure
The initialization process will generate the skeleton with folders and files. Our new extension's structure looks like this:
```
restaurants/
├ app/
| ├ node_modules/
| ├ extension.js
| ├ index.js
| └ package.json
├ server/
| ├ node_modules/
| └ package.json
└ extension.json
```
Let's explain the structure:
- `app/`: Folder where you keep your mobile app side code (this will be bundled into the app)
- `server/`: Folder where you keep your server side code and assets
- `extension.json`: File that describes your extension
Specific parts will be explained soon.
In `extension.json` you can see:
```JSON
#file: extension.json
{
"name": "restaurants",
"version": "0.0.1",
"title": "Restaurants",
"description": "A restaurants extension.",
"platform": "1.0.*"
}
```
Brief property explanations:
- `name` uniquely identifies the extension when combined with your developer name (e.g. `{{ site.example.devName }}.restaurants`)
- `version` is the extension version
- `platform` indicates the version of the [plaform]({{ site.url }}/docs/extensions/reference/platform) (versions of React, React Native and other packages available to all extensions by default)
- `title` and `description` are extension descriptors
We also uploaded our extension to Shoutem:
```ShellSession
$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!
```
And installed it into our app:
```ShellSession
$ shoutem install
Extension installed.
See it in the builder: {{ site.shoutem.builderURL }}/app/{{ site.example.appId }}
```
Uploading the extension is self-explanatory, but let's elaborate on installing and uninstalling extensions. In the Builder, you can go to the `Extensions` tab to see which extensions are installed in your app. If you successfully installed your Restaurants extension from [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started), you should see it there under the `Custom` category.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/extension-tab-extension.png'/>
</p>
Extensions are installed into specific apps, not all apps on your account.
Now let's elaborate on [screens and shortcuts]({{ site.url }}/docs/extensions/my-first-extension/shortcut-and-screen).
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-04-CreatingShortcutAndScreen.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/shortcut-and-screen
title: Creating a Screen and Shortcut
section: My first extension
---
# Creating a Screen and Shortcut
Extensions can have multiple screens in the app. Screens are [React components](https://facebook.github.io/react/docs/react-component.html) that represent a mobile screen. We want our Restaurants extension to have 2 screens; one for the list of the restaurants (which we already made in [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started)) and another for the details of each particular restaurant the user taps.
Since the app needs to know which screen to open first for some extension, we need to create a ***shortcut*** when creating that screen. A shortcut is a link to the starting screen of an extension. It's the item in the Main Navigation which opens the starting screen when a user taps on it.
We created the List screen with a shortcut in [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started) using:
```ShellSession
$ shoutem screen add List
? Screen name: List
? Create a shortcut (so that screen can be added through the Builder)? Yes
? Shortcut name: {{ site.example.extensionName }}
? Shortcut title: Restaurants
? Shortcut description: A shortcut for List
...
Success
```
The CLI modified the `extension.json` to include the screen and it's shortcut:
```json{7-14}
#file: extension.json
{
"name": "restaurants",
"version": "0.0.1",
"platform": "1.0.*",
"title": "Restaurants",
"description": "List of restaurants",
"screens": [{
"name": "List"
}],
"shortcuts": [{
"name": "Restaurants",
"title": "Restaurants"
"screen": "@.List"
}]
}
```
They were added inside arrays. The `name` property uniquely identifies these extension parts. A shortcut's `title` is what will be shown in the Main Navigation (in the Builder and in the app). The `screen` property inside `shortcuts` references the screen that will open when a user taps on that shortcut in navigation.
When referencing any extension part, we need to say which extension it came from. The full name of extension part follows this structure: `<developer-name>.<extension-name>.<extension-part-name>` (e.g. `{{ site.example.devName }}.restaurants.List)`. For extension parts within the same extension, you can just use `@.<extension-part-name>` (e.g. `@.List`). `@.` stands for `<developer-name>.<extension-name>.` of the current extension.
The Shoutem CLI also created `app/screens/` folder with a `List.js` file, which you edited in [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started).
## How do Extensions fit into an App?
The `app` folder from your extension will be bundled (along with the rest of the extensions your app uses) into the full React Native app. For your extension to use an `npm` package, just install it inside the `app` folder.
Below is an example of installing [React Native swiper](https://github.com/leecade/react-native-swiper) (just an example, no need to execute following 2 commands).
Locate to the `app` folder and install the package with saving the dependency in the `package.json`:
```ShellSession
$ cd app/
$ npm install --save react-native-swiper
```
This package would be installed when bundling your extension into the app. You would be able to access it in any file in the `app` folder.
## Exporting Extension Parts
The app expects extensions to export their parts (e.g. screens) in `app/index.js` (that's standard JS practice). Extensions are like libraries and other extensions can reuse what they export from `app/index.js`. The convention is that `app/index.js` is the public API of an extension and shouldn't be changed often.
The current `index.js` looks like this:
```JSX
#file: app/index.js
// Reference for app/index.js can be found here:
// http://shoutem.github.io/docs/extensions/reference/extension-exports
import * as extension from './extension.js';
export const screens = extension.screens;
export const themes = extension.themes;
```
On the other hand, `app/extension.js` is managed by the CLI and you should not change it. When creating screens, the CLI writes their location in `app/extension.js` which are exported in `app/index.js`.
## Previewing Extension Code Changes
We already did this in [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started), but let's elaborate on it. Since the app is managed through the Builder, we needed to `push` the extension to Shoutem and `install` it into our app so we can use and preview it in the Builder.
We then opened the app in the Builder and added the extension's screen to Main navigation. Installing new extensions and adding their shortcuts to the app requires you to reconfigure your local clone, which we also did using `shoutem configure`.
Let's preview your app again. We can preview it in the Builder, but it might take some time while the Builder bundles the entire app again. Every time you change an extension, you'd have to _push_ it again and then the Builder would need to re-bundle the whole app to add the changes. It's much faster to [set up your local environment]({{ site.url }}/docs/extensions/tutorials/setting-local-environment) and simply use `react-native run-ios` or `react-native run-android`.
Let's preview the app and see where we stopped in [Getting Started]({{ site.url }}/docs/extensions/tutorials/getting-started).
```ShellSession
$ react-native run-ios
Scanning folders for symlinks in /path/to/Restaurants/node_modules
...
```
<p class="image">
<img src='{{ site.url }}/img/tutorials/getting-started/03-lets-eat.png'/>
</p>
> #### Note
> In the documentation the preview you see is from the Builder, instead of a screenshot from the Shoutem Preview app or a local emulator. This way you'll see the state of the web interface as well.
Now let's make a quick change to the app code so you can see it change in real time on the emulator. Open your `restaurants` extension's `List.js` screen file and add another line of text:
```JavaScript{6}
#file: app/screens/List.js
export default class List extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>Let's eat!</Text>
<Text style={styles.text}>Can't do anything on an empty stomach!</Text>
</View>
);
}
}
```
After reloading the emulator, your new line of text should be visible immediately:
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/real-time-preview.png'/>
</p>
Your extension only has a simple screen right now, let's add some [UI components]({{ site.url }}/docs/extensions/my-first-extension/using-ui-toolkit).
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-05-UsingUIToolkit.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/using-ui-toolkit
title: Using UI toolkit
section: My first extension
---
# Using the UI Toolkit
React Native exposes plain iOS and Android native components that you can use, but there's usually a lot of work left to do just to make them look beautiful. Instead, you can use [@shoutem/ui](https://github.com/shoutem/ui), a set of customizable UI components. There are [plenty of components]({{ site.url }}/docs/ui-toolkit/components/typography) that you can use out of the box.
## Creating a Restaurants List
Let's create a list of restaurants. Start by importing UI components from the toolkit.
```javascript{9-17,19}
#file: app/screens/List.js
import React, { PureComponent } from 'react';
import { StyleSheet } from 'react-native';
import { NavigationBar } from 'shoutem.navigation';
import {
ImageBackground,
ListView,
Tile,
Title,
Subtitle,
Overlay,
Screen
} from '@shoutem/ui';
```
Notice that you didn't need to install the `@shoutem/ui` package into the `app` folder of your extension. That's because this package will be installed in the extension by the app into which your extension is bundled. All packages installed by the app by default can be found in `peerDependencies` of `app/package.json`. Also, we removed `View` and `Text` from the `react-native` import.
We prepared some mockup restaurants data for you. Download [this compressed file](/static/getting-started/restaurants.zip), extract it and copy the extracted `assets` folder into your `app` folder. The `assets` folder contains static restaurants data in `restaurants.json`.
Define a method in your `List` class that returns an array of restaurants.
```javascript{3-5}
#file: app/screens/List.js
export default class List extends PureComponent {
getRestaurants() {
return require('../assets/restaurants.json');
}
```
Implement a `render` method that will use a `ListView` component. [ListView]({{ site.url }}/docs/ui-toolkit/components/list-view) accepts `data` in the form of an `array` to be shown in the list and `renderRow` is a callback function that defines how each row in the list should look.
Add the `renderRow` method and change the implementation of the `render` method:
```JSX{3-14,17-25}
#file: app/screens/List.js
getRestaurants() {...}
// defines the UI of each row in the list
renderRow(restaurant) {
return (
<ImageBackground styleName="large-banner" source={% raw %}{{ uri: restaurant.image &&
restaurant.image.url ? restaurant.image.url : undefined }}{% endraw %}>
<Tile>
<Title>{restaurant.name}</Title>
<Subtitle>{restaurant.address}</Subtitle>
</Tile>
</ImageBackground>
);
}
render() {
return (
<Screen>
<NavigationBar title="RESTAURANTS" />
<ListView
data={this.getRestaurants()}
renderRow={restaurant => this.renderRow(restaurant)}
/>
</Screen>
);
}
```
Since we've only changed the app code now, we don't need to upload the extension. However, in case you're checking the changes in the Builder, do:
```ShellSession
$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!
```
The app preview will be shown after Shoutem bundles the new app. `List` is now showing the list of restaurants.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/extension-rich-list.png'/>
</p>
This looks exactly how we wanted.
Try clicking on a one of the restaurants. Nothing happens! We want to open up the screen with a restaurant's details when the user touches a row in the list.
## Creating a Details Screen
When a restaurant in the list is touched, we will open the details screen for that restaurant. To make components respond to touches, use the [TouchableOpacity](https://facebook.github.io/react-native/docs/touchableopacity.html) component from React Native. We'll also import Shoutem's `navigateTo` action creator to navigate to another screen and the `ext` function for the name of screen we're navigating to.
Let's import these things (find the complete code below):
```javascript{1-5}
#file: app/screens/List.js
import { TouchableOpacity } from 'react-native';
import { navigateTo } from 'shoutem.navigation';
import { ext } from '../const';
```
[Connect](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) `navigateTo` action creator to redux store.
```javascript{1,3-9,14-18}
#file: app/screens/List.js
import { connect } from 'react-redux';
export class List extends PureComponent {
constructor(props) {
super(props);
// bind renderRow function to get the correct props
this.renderRow = this.renderRow.bind(this);
}
getRestaurants() {...}
}
// connect screen to redux store
export default connect(
undefined,
{ navigateTo }
)(List);
```
> #### Note
> Make sure that you remove the `default` from `export default class List extends PureComponent` because there can only be one default export and we want `export default connect` to be it.
Now create the Details screen:
```ShellSession
$ shoutem screen add Details
? Screen name: Details
? Create a shortcut (so that screen can be added through the Builder)? No
Success
```
We didn't create a `shortcut` since this screen isn't the starting screen your extension.
Open the restaurants details screen in the `renderRow` function. The `navigateTo` action creator accepts Shoutem `route object` as the only argument with `screen` (full name of screen to navigate to) and `props` (passed to screen) properties. To get the full name of the screen, we'll use the `ext` function, which returns the full name of the extension part passed as its first argument (e.g. returns `tom.restaurants.Details` for `Details`) or the full extension name (e.g. `tom.restaurants`) if no argument is passed.
```JSX{2,5-8,16}
#file: app/screens/List.js
renderRow(restaurant) {
const { navigateTo } = this.props;
return (
<TouchableOpacity onPress={() => navigateTo({
screen: ext('Details'),
props: { restaurant }
})}>
<ImageBackground styleName="large-banner" source={% raw %}{{ uri: restaurant.image &&
restaurant.image.url ? restaurant.image.url : undefined }}{% endraw %}>
<Tile>
<Title>{restaurant.name}</Title>
<Subtitle>{restaurant.address}</Subtitle>
</Tile>
</ImageBackground>
</TouchableOpacity>
);
}
```
This is what you should end up with in `app/screens/List.js`:
```JSX
#file: app/screens/List.js
import React, { PureComponent } from 'react';
import { TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { navigateTo, NavigationBar } from 'shoutem.navigation';
import {
ImageBackground,
ListView,
Tile,
Title,
Subtitle,
Overlay,
Screen
} from '@shoutem/ui';
import { ext } from '../const';
export class List extends PureComponent {
constructor(props) {
super(props);
// bind renderRow function to get the correct props
this.renderRow = this.renderRow.bind(this);
}
getRestaurants() {
return require('../assets/restaurants.json');
}
// defines the UI of each row in the list
renderRow(restaurant) {
const { navigateTo } = this.props;
return (
<TouchableOpacity onPress={() => navigateTo({
screen: ext('Details'),
props: { restaurant }
})}>
<ImageBackground styleName="large-banner" source={% raw %}{{ uri: restaurant.image &&
restaurant.image.url ? restaurant.image.url : undefined }}{% endraw %}>
<Tile>
<Title>{restaurant.name}</Title>
<Subtitle>{restaurant.address}</Subtitle>
</Tile>
</ImageBackground>
</TouchableOpacity>
);
}
render() {
return (
<Screen>
<NavigationBar title="RESTAURANTS" />
<ListView
data={this.getRestaurants()}
renderRow={restaurant => this.renderRow(restaurant)}
/>
</Screen>
);
}
}
// connect screen to redux store
export default connect(
undefined,
{ navigateTo }
)(List);
```
For the `Details` screen just copy the following code. We're not introducing any new concept here, just using some additional components.
```JSX
#file: app/screens/Details.js
import React, { PureComponent } from 'react';
import { ScrollView } from 'react-native';
import {
Icon,
Row,
Subtitle,
Text,
Title,
View,
ImageBackground,
Divider,
Tile,
} from '@shoutem/ui';
export default class Details extends PureComponent {
render() {
const { restaurant } = this.props;
return (
<ScrollView style = {% raw %}{{marginTop:-70}}{% endraw %}>
<ImageBackground styleName="large-portrait" source={% raw %}{{ uri: restaurant.image &&
restaurant.image.url ? restaurant.image.url : undefined }}{% endraw %}>
<Tile>
<Title>{restaurant.name}</Title>
<Subtitle>{restaurant.address}</Subtitle>
</Tile>
</ImageBackground>
<Row>
<Text>{restaurant.description}</Text>
</Row>
<Divider styleName="line" />
<Row>
<Icon name="laptop" />
<View styleName="vertical">
<Subtitle>Visit webpage</Subtitle>
<Text>{restaurant.url}</Text>
</View>
<Icon name="right-arrow" />
</Row>
<Divider styleName="line" />
<Row>
<Icon name="pin" />
<View styleName="vertical">
<Subtitle>Address</Subtitle>
<Text>{restaurant.address}</Text>
</View>
<Icon name="right-arrow" />
</Row>
<Divider styleName="line" />
<Row>
<Icon name="email" />
<View styleName="vertical">
<Subtitle>Email</Subtitle>
<Text>{restaurant.mail}</Text>
</View>
</Row>
<Divider styleName="line" />
</ScrollView>
);
}
}
```
Now when you reload the app and tap on a restaurant in the list, this is what you get:
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/extension-rich-details.png'/>
</p>
If you aren't previewing with an [emulator on your local machine]({{ site.url }}/docs/extensions/tutorials/setting-local-environment), you will have to _push_ the extension:
```ShellSession
$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!
```
Looking at the preview, that's exactly what we wanted. However, your app is using static data. Let's connect it to the **Shoutem Cloud**.
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-06-UsingCloudStorage.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/using-cloud-storage
title: Using Cloud Storage
section: My first extension
---
# Using Cloud Storage
Shoutem Cloud Storage is a CMS solution for mobile apps. We made the [@shoutem/redux-io](https://github.com/shoutem/redux-io) package to simplify the communication with Shoutem CMS. Define a `data schema` to describe your data model, do this in your Restaurant extension directory:
```ShellSession
$ shoutem schema add Restaurants
Schema `Restaurants` is created in file `server/data-schemas/Restaurants.json`!
File `extension.json` was modified.
```
The CLI just created a `data-schemas` folder inside the `server` folder and put `Restaurants.json` in it, which contains the following:
```JSON
#file: server/data-schemas/Restaurants.json
{
"title": "Restaurants",
"properties": {
"name": {
"format": "single-line",
"title": "Name",
"type": "string",
"displayPriority": 1
},
},
"titleProperty": "name",
"type": "object"
}
```
We're using the `server` folder because data schemas are not part of the app code, but rather the server side of an extension. Data schemas are nothing more than Shoutem-flavored [JSON Schemas](http://json-schema.org/). They describe the data being stored on Shoutem Cloud Storage.
All fields are explained in the [data schema reference]({{ site.url }}/docs/cloud/data-schemas). This schema is exported in `extension.json`:
```JSON{18-21}
#file: extension.json
{
"name": "restaurants",
"version": "0.0.1",
"platform": "1.0.*",
"title": "Restaurants",
"description": "A restaurants extension.",
"screens": [{
"name": "List"
}, {
"name": "Details"
}],
"shortcuts": [{
"name": "Restaurants",
"title": "Restaurants",
"description": "Allow users to browse through list of restaurants"
"screen": "@.List",
}],
"dataSchemas": [{
"name": "Restaurants",
"path": "server/data-schemas/Restaurants.json"
}]
}
```
Currently, your schema only has the `name` property, which we'll use for each restaurants name. Let's add additional properties which we want to have for each restaurant, such as: `address`, `description`, website `url`, `image` and `mail`.
```JSON{4-40}
#file: server/data-schemas/Restaurants.json
{
"title": "Restaurant",
"properties": {
"name": {
"format": "single-line",
"title": "Restaurant's name",
"type": "string",
"displayPriority": 1
},
"address": {
"format": "single-line",
"title": "Address",
"type": "string",
"displayPriority": 2
},
"description": {
"format": "multi-line",
"title": "Description",
"type": "string",
"displayPriority": 3
},
"url": {
"format": "uri",
"title": "Website",
"type": "string",
"displayPriority": 4
},
"image": {
"format": "attachment",
"title": "Image",
"type": "object",
"referencedSchema": "shoutem.core.image-attachments",
"displayPriority": 5
},
"mail": {
"format": "single-line",
"title": "E-mail",
"type": "string",
"displayPriority": 6
}
},
"titleProperty": "name",
"type": "object"
}
```
To enter data for your schema, you need to use settings page. Basically, the [settings pages]({{ site.url }}/docs/extensions/tutorials/settings-pages-introduction) are web pages on the Builder. Extension developers write them to enable app owners to manage their extensions.
Shoutem prepared a CMS settings page inside the [shoutem.cms](https://github.com/shoutem/extensions/tree/master/shoutem-cms) extension that you can use to manage data for your `schema` on the Shoutem Cloud. Reference that settings page in the `Restaurants` shortcut and pass it the `Restaurants` schema. The page will appear when an app owner selects the `Restaurants` shortcut on the Builder:
```JSON{17-23}
#file: extension.json
{
"name": "restaurants",
"version": "0.0.1",
"platform": "1.0.*",
"title": "Restaurants",
"description": "List of restaurants",
"screens": [{
"name": "List"
}, {
"name": "Details"
}],
"shortcuts": [{
"name": "Restaurants",
"title": "Restaurants",
"description": "Allow users to browse through list of restaurants",
"screen": "@.List",
"adminPages": [{
"page": "shoutem.cms.CmsPage",
"title": "Content",
"parameters": {
"schema": "@.Restaurants"
}
}]
}],
"dataSchemas": [{
"name": "Restaurants",
"path": "server/data-schemas/Restaurants.json"
}]
}
```
Let's upload the extension now, since we want to customize the web interface and the extension server side:
```ShellSession
$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!
```
Go to the [Builder]({{ shoutem.builderURL }}), open your app and select `Restaurants` under `Main navigation` to see the Shoutem CMS page.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/empty-cms-page.png'/>
</p>
Click on `Create Items` to start adding content. This will open a modal that contains the `CMS` interface, where you can manage the content for your extension. Apps that get content from the Shoutem CMS will immediately show new content once you edit or add it.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/empty-cms.png'/>
</p>
Click on `Add item`. This will open a modal for inserting data for the `Restaurants` model, which you defined with your data schema.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/cms-modal.png'/>
</p>
Add at least one restaurant. Now you can see the data in the CMS settings page of your Restaurants extension:
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/full-cms.png'/>
</p>
Although you've added some restaurants in the Builder, your extension is still coded to use static data from the `assets` folder you set up earlier in the tutorial series. Let's change that and start fetching the data from Shoutem Cloud Storage using the `@shoutem/redux-io` package.
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-07-WorkingWithData.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/working-with-data
title: Working with Data
section: My first extension
---
# Working with Data
Let's fetch data from the Shoutem Cloud storage to the extension. First, remove the `app/assets` folder, we don't need it anymore. Also remove the `getRestaurants()` function from `List.js`.
```JavaScript{2-4}
//remove this:
getRestaurants() {
return require('../assets/restaurants.json');
}
```
Now create a `reducer.js` file in the `app` folder.
```ShellSession
$ cd app
$ touch reducer.js
```
This file will contain a `reducer` defining the initial app state and how the state changes.
Our [@shoutem/redux-io](https://github.com/shoutem/redux-io) package has `reducers` and `actions` that communicate with the Shoutem CMS. The `storage` reducer retrieves data (eg. restaurants) into a dictionary, while `collection` stores data ID's in an array to persist its order.
```javascript{1-9}
#file: app/reducer.js
import { storage, collection } from '@shoutem/redux-io';
import { combineReducers } from 'redux';
import { ext } from './const';
// combine reducers into one root reducer
export default combineReducers({
restaurants: storage(ext('Restaurants')),
allRestaurants: collection(ext('Restaurants'), 'all')
});
```
We've used the `ext` function to get the full schema name (`{{ site.example.devName }}.restaurants.Restaurants`). The root reducer needs to be exported from `app/index.js` as `reducer`, so your app can find it:
```javascript{4,11}
#file: app/index.js
// Reference for app/index.js can be found here:
// http://shoutem.github.io/docs/extensions/reference/extension-exports
import reducer from './reducer';
import * as extension from './extension.js';
export const screens = extension.screens;
export const themes = extension.themes;
export { reducer };
```
Find more information about extension parts [here]({{ site.url }}/docs/extensions/reference/extension-exports).
We will fetch restaurants from **Shoutem Cloud Storage** in the `List` screen with the `find` action creator. Also, we'll use three helper functions from our `@shoutem/redux-io` package:
```javascript{1-6}
#file: app/screens/List.js
import {
find,
isBusy,
shouldRefresh,
getCollection
} from '@shoutem/redux-io';
```
- `isBusy` - data is being fetched,
- `shouldRefresh` - should data be (re)fetched,
- `getCollection` - merges `storage` dictionary and `collection` ID array into an `array` of objects.
The complete code is for `app/screens/List.js` is available below.
Fetch data in the `componentDidMount` lifecycle method.
```javascript{2-10}
#file: app/screens/List.js
export class List extends PureComponent {
componentDidMount() {
const { find, restaurants } = this.props;
if (shouldRefresh(restaurants)) {
find(ext('Restaurants'), 'all', {
include: 'image',
})
}
}
...
}
```
Implement rendering with fetched data.
```JSX{2,8-9}
#file: app/screens/List.js
render() {
const { restaurants } = this.props;
return (
<Screen>
<NavigationBar title="RESTAURANTS" />
<ListView
data={restaurants}
loading={isBusy(restaurants)}
renderRow={restaurant => this.renderRow(restaurant)}
/>
</Screen>
);
}
```
Once fetched, restaurants will go into the app state. Convert them to an array with `getCollection` and then connect `find` to redux store.
```javascript{2-6}
#file: app/screens/List.js
export default connect(
(state) => ({
// get an array of restaurants from allRestaurants collection
restaurants: getCollection(state[ext()].allRestaurants, state)
}),
{ navigateTo, find }
)(List);
```
This is the final result of `List` screen:
```JSX
#file: app/screens/List.js
import React, { PureComponent } from 'react';
import { TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { navigateTo, NavigationBar } from 'shoutem.navigation';
import {
find,
isBusy,
shouldRefresh,
getCollection
} from '@shoutem/redux-io';
import {
ImageBackground,
ListView,
Tile,
Title,
Subtitle,
Overlay,
Screen
} from '@shoutem/ui';
import { ext } from '../const';
export class List extends PureComponent {
constructor(props) {
super(props);
// bind renderRow function to get the correct props
this.renderRow = this.renderRow.bind(this);
}
componentDidMount() {
const { find, restaurants } = this.props;
if (shouldRefresh(restaurants)) {
find(ext('Restaurants'), 'all', {
include: 'image',
})
}
}
// defines the UI of each row in the list
renderRow(restaurant) {
const { navigateTo } = this.props;
return (
<TouchableOpacity onPress={() => navigateTo({
screen: ext('Details'),
props: { restaurant }
})}>
<ImageBackground styleName="large-banner" source={% raw %}{{ uri: restaurant.image &&
restaurant.image.url ? restaurant.image.url : undefined }}{% endraw %}>
<Tile>
<Title>{restaurant.name}</Title>
<Subtitle>{restaurant.address}</Subtitle>
</Tile>
</ImageBackground>
</TouchableOpacity>
);
}
render() {
const { restaurants } = this.props;
return (
<Screen>
<NavigationBar title="RESTAURANTS" />
<ListView
data={restaurants}
loading={isBusy(restaurants)}
renderRow={restaurant => this.renderRow(restaurant)}
/>
</Screen>
);
}
}
// connect screen to redux store
export default connect(
(state) => ({
// get an array of restaurants from allRestaurants collection
restaurants: getCollection(state[ext()].allRestaurants, state)
}),
{ navigateTo, find }
)(List);
```
>#### Note
>Make sure you remove the `default` from `export default class List extends Component` and only have `default` in `export default connect`, because there can only be one default export.
Let's check how it works:
```ShellSession
$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!
```
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/working-with-data.png'/>
</p>
Works like a charm! You just made your first extension using the **Shoutem UI Toolkit** and **Shoutem Cloud Storage**. Great job!
================================================
FILE: docs/extensions/my-first-extension/_posts/1970-01-08-Publish.md
================================================
---
layout: doc
permalink: /docs/extensions/my-first-extension/publish
title: Publish your extension and app
section: My first extension
---
# Publish Your Extension and App!
Once you're satisfied with your extension, you can publish it. Publishing an extension freezes that version, so no changes can be made to that version, only to the new versions. At the moment, every extension that you publish to the market is only visible to you.
To publish an app to Google Play and App Store, every extension in the app needs to be published. So, let's publish our extension!
```ShellSession
$ shoutem publish
Publishing `Restaurants` extension to Shoutem...
Version `0.0.1` of `Restaurants` extensions was published!
```
Publish your app now to the stores. You can let Shoutem publish it for you to both stores (premium feature) or publish it manually following [our tutorial]({{ site.url }}/docs/extensions/tutorials/publish-your-app). Shoutem has developed an easy and intuitive process of app publishing. Simply insert information at one place and Shoutem will take care of the rest.
<p class="image">
<img src='{{ site.url }}/img/tutorials/setting-local-environment/settings.png'/>
</p>
## Using the app dashboard!
Once the app hits the stores, the dashboard really starts to shine. You can manage your mobile app and resubmit the changes over the wire. You can even grant your clients access to the dashboard, so they can manage the app for themselves.
To send push notifications, navigate to the _Push_ tab in the Builder and customize the push notification you want to send.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/push-notification.png'/>
</p>
Tracking what users are doing within your app is also in the palm of your hand with our _Stats_ tab.
<p class="image">
<img src='{{ site.url }}/img/my-first-extension/analytics.png'/>
</p>
## What's next?
Get to work making gorgeous native apps!
Check out these resources:
- Read the [technical overview]({{ site.url }}/docs/extensions/tutorials/architecture-and-best-practises) for Shoutem extensions
- Build beautiful apps with the Shoutem [UI toolkit]({{ site.url }}/docs/ui-toolkit/introduction)!
- Use our [React Native school]({{ site.shoutem.school }}) to learn more, it has lectures for everyone!
Happy coding!
================================================
FILE: docs/extensions/reference/_posts/1970-01-01-ExtensionFile.md
================================================
---
layout: doc
permalink: /docs/extensions/reference/extension
title: Extension file format
section: Reference
---
# Extension file format
The main file that describes every extension is `extension.json`, which is located in the root folder of the extension.
## Structure of extension.json
Following structure shows only `root` fields of the extension.json. Detailed description about each of those fields is below.
```json
{
// required
"name": "restaurants",
"version": "0.0.1",
"platform": "1.0.*",
// recommended
"title": "Restaurants",
"description": "List restaurants in your app",
"website": "https://www.shoutem.com/restaurants",
"icon": "server/assets/extension/icon.png",
// optional
"settingsPages": [{...}],
"settings": {...},
// optional exports (extension parts)
"shortcuts": [{...}],
"screens": [{...}],
"dataSchemas": [{...}],
"pages": [{...}],
"themes": [{...}],
"themeVariables": [{...}]
}
```
## Defining and referencing extension parts
As you see in _Structure of extension.json_ chapter, extension exports multiple extension parts (shortcuts, screens, dataSchemas, pages, themes, themeVariables). In order to be able to use these extension parts, we need to define them, so we can later reference them in other parts. Defining is done in `name` field which value needs to be unique for that extension part (name `List` can be only used for 1 shortcut, but also for 1 screen, etc.).
On the other hand, when referencing extension parts, fully qualified name needs to be used. Fully qualified name of **extension** is done by prefixing `<developer-name>.` to `name` field (for `restaurants` extension developed by `shoutem`, extension would have unique identifier `shoutem.restaurants`). Fully qualified name of **extension parts** is done by suffixing `<developer-name>.<extension-name>.` with the unique identifier for that extension part, e.g. `shoutem.restaurants.List` for shortcut. If you're referencing the extension part from within the same extension, use `@.` instead of `<developer-name>.<extension-name>.` (e.g. `@.List`).
## Fields
Here you can find field explanations in the same order fields appeared in the upper example:
#### name
Required field. Defines extension's identity. Must be unique among your extensions and not longer than 32 characters.
#### version
Required field. Version of your extension.
#### platform
Required field. Version of [Shoutem platform]({{ site.url }}/docs/extensions/reference/platform), which defines versions of React, React Native, Redux and some other packages.
#### title
Title of your extension.
#### description
Description of your extension.
#### website
Website that promotes your extension.
#### icon
Path to extension's icon that will be present in Shoutem Extension Market. Store the icon in `server` asset's folder, as it will be used on Shoutem's server side.
#### settingsPages
Array of [extension settings pages]({{ site.url }}/docs/extensions/reference/settings-types) used to manage the global settings of the extension.
```json
[{
// required
"page": "@.Settings",
// recommended
"title": "Settings",
// optional
"parameters": {
"any-parameter": "any-value"
}
}]
```
Each object in settings pages array, settings page object, consist of these fields:
- `page`: Required field, references the extension page
- `title`: Title of extension page
- `parameters`: Dictionary of arbitrary key/value pairs that will be passed to extension settings page
#### settings
Dictionary of arbitrary key/value pairs that represent **default** extensions's settings passed to settings pages objects.
```json
{
"any-parameter": "any-value"
}
```
#### shortcuts
[Shortcuts]({{ site.url }}/docs/extensions/my-first-extension/shortcut-and-screen) are links to the starting screen of your extension. Format:
```json
[{
// required
"name": "List",
// required (pick one)
"screen": "@.List",
"action": "@.visitRestaurants"
// recommended
"title": "Restaurants",
"description": "Allow users...",
"icon": "server/assets/shortcuts/restaurants-list.png",
// optional
"type": "navigation",
"adminPages": [{
// required
"page": "@.CmsPage",
// recommended
"title": "Content",
// optional
"parameters": {
"schema": "@.Restaurants"
},
}],
"settings": {
"any-parameter": "any-value"
}
}]
```
Each object in shortcuts array, shortcut object, consists of these fields:
- `name`: Required field, defines shortcut's identity
- `screen/action`: Shortcut can either open a `Screen` or call an `Action` (see example in [Shoutem Auth](https://github.com/shoutem/extensions/blob/master/shoutem-auth/extension.json) extension)
- `title`: Shortcut's title
- `description`: Shortcut's description
- `icon`: Path to shortcut's icon that will be shown in builder. Store in `server` asset's folder
- `type`: Indicates the type of shortcut. It can be `navigation` or `undefined`. If `navigation`, it will be possible to nest other shortcuts below the current
- `adminPages`: Array of shortcut's admin pages. Admin page object inside of array consists of:
- `page`: Required field, references a [settings page]({{ site.url }}/docs/extensions/tutorials/settings-pages-introduction)
- `title`: Title of admin page
- `parameters` Dictionary of arbitrary key/value pairs that will be passed to admin page instance
- `settings`: Dictionary of arbitrary key/value pairs that represent default Shortcut's settings passed to admin pages
#### screens
Screens are nothing more than React components which represent full mobile screen. Format:
```json
[{
// required
"name": "List",
// recommended
"title": "List",
"image": "server/assets/screens/restaurants-list.png",
// optional
"navigatesTo": [{
"details": "@.Details"
}],
"settingsPage": {
// required
"page": "@.List",
// optional
"parameters": {
"any-parameter": "any-value"
}
},
"settings": {
"any-parameter": "any-value"
}
}, {
"name": "Grid",
"title": "Grid",
"image": "server/assets/screens/restaurants-grid.png",
"extends": "@.List",
"settingsPage": {
"page": "@.List",
"parameters": {
"any-parameter": "any-value"
}
},
}]
```
Each object in screens array, screen object, consists of these fields:
- `name`: Required field, defines screen's identity
- `title`: Screen's title that will be shown in [layout selector]({{ site.url }}/docs/extensions/tutorials/screen-layouts)
- `image`: Path to screen's image that shows it's layout
- `navigatesTo`: Array of key/value pairs that indicates to which screens the current one can navigate to
- `settingsPage`: Screen's settings page. Object consists of:
- `page`: Required field, references an [settings page]({{ site.url }}/docs/extensions/tutorials/settings-pages-introduction)
- `parameters`: Dictionary of arbitrary key/value pairs that will be passed to settings page instance
- `settings`: Dictionary of arbitrary key/value pairs that represent default Shortcut's settings passed to admin pages
- `extends`: References screen that the current one is extending
In the example above, we included 2 screen objects inside of the `screens` array. We wanted to show you the usage of `extends` field. Extending makes it possible to [switch between mu
gitextract_rdoazj5u/
├── .gitignore
├── Gemfile
├── README.md
├── _config.yml
├── _includes/
│ ├── cards.html
│ ├── footer.html
│ ├── ga.html
│ ├── head.html
│ ├── home-header-video.html
│ ├── navbar.html
│ ├── overview-content.html
│ ├── sidebar-nav.html
│ └── signup-modal.html
├── _layouts/
│ ├── doc.html
│ ├── home.html
│ └── overview.html
├── _sass/
│ ├── animation.scss
│ ├── base.scss
│ ├── cards.scss
│ ├── colors.scss
│ ├── documentation-layout.scss
│ ├── footer.scss
│ ├── github.scss
│ ├── helpers.scss
│ ├── menu-overlay.scss
│ ├── mixins.scss
│ ├── navbar.scss
│ ├── pager.scss
│ ├── sidebar.scss
│ ├── typography.scss
│ └── variables.scss
├── css/
│ ├── documentation.scss
│ ├── home.scss
│ ├── prism.css
│ └── style.scss
├── csv/
│ └── restaurants.csv
├── docs/
│ ├── cloud/
│ │ └── _posts/
│ │ ├── 1970-01-01-ShoutemCloud.md
│ │ └── 1970-01-02-DataSchemas.md
│ ├── extensions/
│ │ ├── my-first-extension/
│ │ │ └── _posts/
│ │ │ ├── 1970-01-01-Introduction.md
│ │ │ ├── 1970-01-03-InitializingExtension.md
│ │ │ ├── 1970-01-04-CreatingShortcutAndScreen.md
│ │ │ ├── 1970-01-05-UsingUIToolkit.md
│ │ │ ├── 1970-01-06-UsingCloudStorage.md
│ │ │ ├── 1970-01-07-WorkingWithData.md
│ │ │ └── 1970-01-08-Publish.md
│ │ ├── reference/
│ │ │ └── _posts/
│ │ │ ├── 1970-01-01-ExtensionFile.md
│ │ │ ├── 1970-01-01-Overview.md
│ │ │ ├── 1970-01-02-Platform.md
│ │ │ ├── 1970-01-03-ExtensionExports.md
│ │ │ ├── 1970-01-04-SettingsTypesInExtension.md
│ │ │ ├── 1970-01-05-ThemeVariables.md
│ │ │ └── 1970-01-06-CLI.md
│ │ └── tutorials/
│ │ └── _posts/
│ │ ├── 1970-01-01-ConnectToApi.md
│ │ ├── 1970-01-01-GettingStarted.md
│ │ ├── 1970-01-01-PublishYourApp.md
│ │ ├── 1970-01-01-SettingLocalEnvironment.md
│ │ ├── 1970-01-02-WritingATheme.md
│ │ ├── 1970-01-03-ScreenLayouts.md
│ │ ├── 1970-01-04-WritingReactSettingsPage.md
│ │ ├── 1970-01-05-Installing3rdPartyPackages.md
│ │ ├── 1970-01-06-UsingNativeModules.md
│ │ ├── 1970-01-07-ModifyingNativeProject.md
│ │ ├── 1970-01-08-ModifiyingExtensions.md
│ │ ├── 1970-01-09-FAQ.md
│ │ ├── 1970-01-10-DebugSettingsPages.md
│ │ ├── 1970-01-11-WritingHTMLSettingsPages.md
│ │ ├── 1970-01-12-SettingsPageIntro.md
│ │ ├── 1970-01-13-UsingLocalization.md
│ │ ├── 1970-01-14-SettingUpInstagram.md
│ │ ├── 1970-01-15-UsingPatchPackage.md
│ │ ├── 1970-01-16-NavigationIntroduction.md
│ │ ├── 1970-01-17-NavigationBreakingChanges.md
│ │ ├── 1970-01-18-NavigationStacks.md
│ │ └── 1970-01-19-NavigationScreenDecorators.md
│ └── ui-toolkit/
│ ├── animation/
│ │ └── _posts/
│ │ ├── 1970-01-02-Driver.md
│ │ ├── 1970-01-03-Animations.md
│ │ ├── 1970-01-03-FadeIn.md
│ │ ├── 1970-01-04-FadeOut.md
│ │ ├── 1970-01-05-ZoomIn.md
│ │ ├── 1970-01-06-ZoomOut.md
│ │ ├── 1970-01-07-Parallax.md
│ │ └── 1970-01-08-CombiningAnimations.md
│ ├── components/
│ │ └── _posts/
│ │ ├── 1970-01-01-Introduction.md
│ │ ├── 1970-01-02-Typography.md
│ │ ├── 1970-01-03-NavigationBar.md
│ │ ├── 1970-01-04-DropDownMenu.md
│ │ ├── 1970-01-05-ListView.md
│ │ ├── 1970-01-06-GridView.md
│ │ ├── 1970-01-07-Cards.md
│ │ ├── 1970-01-08-Dividers.md
│ │ ├── 1970-01-09-Rows.md
│ │ ├── 1970-01-10-Tiles.md
│ │ ├── 1970-01-11-Spinner.md
│ │ ├── 1970-01-12-Buttons.md
│ │ ├── 1970-01-13-Image.md
│ │ ├── 1970-01-14-Icons.md
│ │ ├── 1970-01-15-View.md
│ │ ├── 1970-01-16-Screen.md
│ │ ├── 1970-01-17-TouchableOpacity.md
│ │ ├── 1970-01-18-Headers.md
│ │ ├── 1970-01-19-Overlay.md
│ │ ├── 1970-01-20-Video.md
│ │ ├── 1970-01-21-Lightbox.md
│ │ ├── 1970-01-22-RichMedia.md
│ │ ├── 1970-01-22-SimpleHtml.md
│ │ ├── 1970-01-23-TextInput.md
│ │ ├── 1970-01-24-ImagePreview.md
│ │ ├── 1970-01-25-ImageGallery.md
│ │ ├── 1970-01-26-InlineGallery.md
│ │ ├── 1970-01-27-Switch.md
│ │ └── 1970-01-29-ImageBackground.md
│ └── theme/
│ └── 1970-01-01-Theme.md
├── index.html
├── jekyll-static.sh
├── js/
│ ├── animation.js
│ ├── docs.js
│ ├── flourish.js
│ ├── main.js
│ └── prism.js
├── lib/
│ └── codemirror/
│ ├── codemirror.css
│ ├── codemirror.js
│ └── mode/
│ ├── javascript/
│ │ ├── index.html
│ │ ├── javascript.js
│ │ ├── json-ld.html
│ │ ├── test.js
│ │ └── typescript.html
│ └── jsx/
│ ├── index.html
│ ├── jsx.js
│ └── test.js
├── static/
│ └── localization/
│ └── en.json
└── video/
├── examples/
│ ├── 01 parallax.webm
│ ├── 02 hero header.webm
│ ├── 03 fade in and out.webm
│ └── 04 zoom in and out.webm
├── header.webm
└── header@2x.webm
SYMBOL INDEX (448 symbols across 10 files)
FILE: js/animation.js
function ShoutemAnimation (line 2) | function ShoutemAnimation(p_container_selector) {
FILE: js/docs.js
function addTargetBlankToLinks (line 96) | function addTargetBlankToLinks() {
function closeSignupModal (line 104) | function closeSignupModal(e)
function handleBackwardTab (line 117) | function handleBackwardTab() {
function handleForwardTab (line 123) | function handleForwardTab() {
function getLocation (line 170) | function getLocation( location ) {
function showMenuItems (line 180) | function showMenuItems() {
function showNavButtons (line 195) | function showNavButtons() {
function prepareCodeblocks (line 236) | function prepareCodeblocks() {
function ajaxLoadLink (line 258) | function ajaxLoadLink (e) {
function setShell$Color (line 302) | function setShell$Color()
function setupVideos (line 313) | function setupVideos()
FILE: js/flourish.js
function Flourish (line 1) | function Flourish ( options )
FILE: js/main.js
function animateDragdealerOnHover (line 81) | function animateDragdealerOnHover( dealer ) {
function onFooterResize (line 149) | function onFooterResize() {
FILE: js/prism.js
function $$ (line 813) | function $$(expr, con) {
function hasClass (line 817) | function hasClass(element, className) {
function highlightLines (line 844) | function highlightLines(pre, lines, classes) {
function applyHash (line 884) | function applyHash() {
FILE: lib/codemirror/codemirror.js
function CodeMirror (line 62) | function CodeMirror(place, options) {
function Display (line 137) | function Display(place, doc, input) {
function loadMode (line 239) | function loadMode(cm) {
function resetModeState (line 244) | function resetModeState(cm) {
function wrappingChanged (line 255) | function wrappingChanged(cm) {
function estimateHeight (line 273) | function estimateHeight(cm) {
function estimateLineHeights (line 291) | function estimateLineHeights(cm) {
function themeChanged (line 299) | function themeChanged(cm) {
function guttersChanged (line 305) | function guttersChanged(cm) {
function updateGutters (line 313) | function updateGutters(cm) {
function updateGutterSpace (line 328) | function updateGutterSpace(cm) {
function lineLength (line 336) | function lineLength(line) {
function findMaxLine (line 355) | function findMaxLine(cm) {
function setGuttersForLineNumbers (line 371) | function setGuttersForLineNumbers(options) {
function measureForScrollbars (line 385) | function measureForScrollbars(cm) {
function NativeScrollbars (line 401) | function NativeScrollbars(place, scroll, cm) {
function maybeDisable (line 473) | function maybeDisable() {
function NullScrollbars (line 494) | function NullScrollbars() {}
function initScrollbars (line 505) | function initScrollbars(cm) {
function updateScrollbars (line 527) | function updateScrollbars(cm, measure) {
function updateScrollbarsInner (line 541) | function updateScrollbarsInner(cm, measure) {
function visibleLines (line 564) | function visibleLines(display, doc, viewport) {
function alignHorizontally (line 589) | function alignHorizontally(cm) {
function maybeUpdateLineNumberWidth (line 608) | function maybeUpdateLineNumberWidth(cm) {
function lineNumberFor (line 626) | function lineNumberFor(options, i) {
function compensateForHScroll (line 633) | function compensateForHScroll(display) {
function DisplayUpdate (line 639) | function DisplayUpdate(cm, viewport, force) {
function maybeClipScrollbars (line 663) | function maybeClipScrollbars(cm) {
function updateDisplayIfNeeded (line 677) | function updateDisplayIfNeeded(cm, update) {
function postUpdateDisplay (line 749) | function postUpdateDisplay(cm, update) {
function updateDisplaySimple (line 778) | function updateDisplaySimple(cm, viewport) {
function setDocumentHeight (line 791) | function setDocumentHeight(cm, measure) {
function updateHeightsInViewport (line 799) | function updateHeightsInViewport(cm) {
function updateWidgetHeight (line 826) | function updateWidgetHeight(line) {
function getDimensions (line 833) | function getDimensions(cm) {
function patchDisplay (line 851) | function patchDisplay(cm, updateNumbersFrom, dims) {
function updateLineForChanges (line 896) | function updateLineForChanges(cm, lineView, lineN, dims) {
function ensureLineWrapped (line 909) | function ensureLineWrapped(lineView) {
function updateLineBackground (line 920) | function updateLineBackground(lineView) {
function getLineContent (line 934) | function getLineContent(cm, lineView) {
function updateLineText (line 947) | function updateLineText(cm, lineView) {
function updateLineClasses (line 962) | function updateLineClasses(lineView) {
function updateLineGutter (line 972) | function updateLineGutter(cm, lineView, lineN, dims) {
function updateLineWidgets (line 1012) | function updateLineWidgets(cm, lineView, dims) {
function buildLineElement (line 1023) | function buildLineElement(cm, lineView, lineN, dims) {
function insertLineWidgets (line 1037) | function insertLineWidgets(cm, lineView, dims) {
function insertLineWidgetsFor (line 1043) | function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
function positionLineWidget (line 1059) | function positionLineWidget(widget, node, lineView, dims) {
function copyPos (line 1089) | function copyPos(x) {return Pos(x.line, x.ch);}
function maxPos (line 1090) | function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
function minPos (line 1091) | function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
function ensureFocus (line 1095) | function ensureFocus(cm) {
function applyTextInput (line 1104) | function applyTextInput(cm, inserted, deleted, sel, origin) {
function handlePaste (line 1151) | function handlePaste(e, cm) {
function triggerElectric (line 1161) | function triggerElectric(cm, inserted) {
function copyableRanges (line 1185) | function copyableRanges(cm) {
function disableBrowserMagic (line 1196) | function disableBrowserMagic(field) {
function TextareaInput (line 1204) | function TextareaInput(cm) {
function hiddenTextarea (line 1223) | function hiddenTextarea() {
function prepareCopyCut (line 1264) | function prepareCopyCut(e) {
function p (line 1405) | function p() {
function prepareSelectAllHack (line 1509) | function prepareSelectAllHack() {
function rehide (line 1522) | function rehide() {
function ContentEditableInput (line 1566) | function ContentEditableInput(cm) {
function onCopyCut (line 1623) | function onCopyCut(e) {
function poll (line 1756) | function poll() {
function posToDOM (line 1876) | function posToDOM(cm, pos) {
function badPos (line 1892) | function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
function domToPos (line 1894) | function domToPos(cm, node, offset) {
function locateNodeInLineView (line 1913) | function locateNodeInLineView(lineView, node, offset) {
function domTextBetween (line 1968) | function domTextBetween(cm, from, to, fromLine, toLine) {
function Selection (line 2018) | function Selection(ranges, primIndex) {
function Range (line 2055) | function Range(anchor, head) {
function normalizeSelection (line 2070) | function normalizeSelection(ranges, primIndex) {
function simpleSelection (line 2086) | function simpleSelection(anchor, head) {
function clipLine (line 2092) | function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.fi...
function clipPos (line 2093) | function clipPos(doc, pos) {
function clipToLen (line 2099) | function clipToLen(pos, linelen) {
function isLine (line 2105) | function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.si...
function clipPosArray (line 2106) | function clipPosArray(doc, array) {
function extendRange (line 2121) | function extendRange(doc, range, head, other) {
function extendSelection (line 2140) | function extendSelection(doc, head, other, options) {
function extendSelections (line 2146) | function extendSelections(doc, heads, options) {
function replaceOneSelection (line 2154) | function replaceOneSelection(doc, i, range, options) {
function setSimpleSelection (line 2161) | function setSimpleSelection(doc, anchor, head, options) {
function filterSelectionChange (line 2167) | function filterSelectionChange(doc, sel, options) {
function setSelectionReplaceHistory (line 2184) | function setSelectionReplaceHistory(doc, sel, options) {
function setSelection (line 2195) | function setSelection(doc, sel, options) {
function setSelectionNoUndo (line 2200) | function setSelectionNoUndo(doc, sel, options) {
function setSelectionInner (line 2212) | function setSelectionInner(doc, sel) {
function reCheckSelection (line 2226) | function reCheckSelection(doc) {
function skipAtomicInSelection (line 2232) | function skipAtomicInSelection(doc, sel, bias, mayClear) {
function skipAtomicInner (line 2247) | function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
function skipAtomic (line 2280) | function skipAtomic(doc, pos, oldPos, bias, mayClear) {
function movePos (line 2293) | function movePos(doc, pos, dir, line) {
function updateSelection (line 2307) | function updateSelection(cm) {
function prepareSelection (line 2311) | function prepareSelection(cm, primary) {
function drawSelectionCursor (line 2330) | function drawSelectionCursor(cm, head, output) {
function drawSelectionRange (line 2349) | function drawSelectionRange(cm, range, output) {
function restartBlink (line 2424) | function restartBlink(cm) {
function startWorker (line 2440) | function startWorker(cm, time) {
function highlightWorker (line 2445) | function highlightWorker(cm) {
function findStartLine (line 2488) | function findStartLine(cm, n, precise) {
function getStateBefore (line 2504) | function getStateBefore(cm, n, precise) {
function paddingTop (line 2522) | function paddingTop(display) {return display.lineSpace.offsetTop;}
function paddingVert (line 2523) | function paddingVert(display) {return display.mover.offsetHeight - displ...
function paddingH (line 2524) | function paddingH(display) {
function scrollGap (line 2533) | function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
function displayWidth (line 2534) | function displayWidth(cm) {
function displayHeight (line 2537) | function displayHeight(cm) {
function ensureLineHeights (line 2545) | function ensureLineHeights(cm, lineView, rect) {
function mapFromLineView (line 2566) | function mapFromLineView(lineView, line, lineN) {
function updateExternalMeasurement (line 2579) | function updateExternalMeasurement(cm, line) {
function measureChar (line 2592) | function measureChar(cm, line, ch, bias) {
function findViewForLine (line 2597) | function findViewForLine(cm, lineN) {
function prepareMeasureForLine (line 2610) | function prepareMeasureForLine(cm, line) {
function measureCharPrepared (line 2632) | function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
function nodeAndOffsetInLineMap (line 2654) | function nodeAndOffsetInLineMap(map, ch, bias) {
function measureCharInner (line 2691) | function measureCharInner(cm, prepared, ch, bias) {
function maybeUpdateRectForZooming (line 2750) | function maybeUpdateRectForZooming(measure, rect) {
function clearLineMeasurementCacheFor (line 2760) | function clearLineMeasurementCacheFor(lineView) {
function clearLineMeasurementCache (line 2769) | function clearLineMeasurementCache(cm) {
function clearCaches (line 2776) | function clearCaches(cm) {
function pageScrollX (line 2783) | function pageScrollX() { return window.pageXOffset || (document.document...
function pageScrollY (line 2784) | function pageScrollY() { return window.pageYOffset || (document.document...
function intoCoordSystem (line 2790) | function intoCoordSystem(cm, lineObj, rect, context) {
function fromCoordSystem (line 2812) | function fromCoordSystem(cm, coords, context) {
function charCoords (line 2829) | function charCoords(cm, pos, context, lineObj, bias) {
function cursorCoords (line 2837) | function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHei...
function estimateCoords (line 2869) | function estimateCoords(cm, pos) {
function PosWithInfo (line 2883) | function PosWithInfo(line, ch, outside, xRel) {
function coordsChar (line 2892) | function coordsChar(cm, x, y) {
function coordsCharInner (line 2913) | function coordsCharInner(cm, lineObj, lineNo, x, y) {
function textHeight (line 2955) | function textHeight(display) {
function charWidth (line 2975) | function charWidth(display) {
function startOperation (line 2997) | function startOperation(cm) {
function fireCallbacksForOps (line 3025) | function fireCallbacksForOps(group) {
function endOperation (line 3042) | function endOperation(cm) {
function endOperations (line 3057) | function endOperations(group) {
function endOperation_R1 (line 3071) | function endOperation_R1(op) {
function endOperation_W1 (line 3084) | function endOperation_W1(op) {
function endOperation_R2 (line 3088) | function endOperation_R2(op) {
function endOperation_W2 (line 3109) | function endOperation_W2(op) {
function endOperation_finish (line 3134) | function endOperation_finish(op) {
function runInOp (line 3181) | function runInOp(cm, f) {
function operation (line 3188) | function operation(cm, f) {
function methodOp (line 3198) | function methodOp(f) {
function docMethodOp (line 3206) | function docMethodOp(f) {
function LineView (line 3221) | function LineView(doc, line, lineN) {
function buildViewArray (line 3233) | function buildViewArray(cm, from, to) {
function regChange (line 3249) | function regChange(cm, from, to, lendiff) {
function regLineChange (line 3314) | function regLineChange(cm, line, type) {
function resetView (line 3328) | function resetView(cm) {
function findViewIndex (line 3336) | function findViewIndex(cm, n) {
function viewCuttingPoint (line 3347) | function viewCuttingPoint(cm, oldN, newN, dir) {
function adjustView (line 3373) | function adjustView(cm, from, to) {
function countDirtyView (line 3394) | function countDirtyView(cm) {
function registerEventHandlers (line 3406) | function registerEventHandlers(cm) {
function dragDropChanged (line 3512) | function dragDropChanged(cm, value, old) {
function onResize (line 3526) | function onResize(cm) {
function eventInWidget (line 3539) | function eventInWidget(display, e) {
function posFromMouse (line 3552) | function posFromMouse(cm, e, liberal, forRect) {
function onMouseDown (line 3573) | function onMouseDown(e) {
function leftButtonDown (line 3615) | function leftButtonDown(cm, e, start) {
function leftButtonStartDrag (line 3642) | function leftButtonStartDrag(cm, e, start, modifier) {
function leftButtonSelect (line 3670) | function leftButtonSelect(cm, e, start, type, addNew) {
function gutterEvent (line 3816) | function gutterEvent(cm, e, type, prevent) {
function clickInGutter (line 3839) | function clickInGutter(cm, e) {
function onDrop (line 3847) | function onDrop(e) {
function onDragStart (line 3906) | function onDragStart(cm, e) {
function onDragOver (line 3929) | function onDragOver(cm, e) {
function clearDragCursor (line 3941) | function clearDragCursor(cm) {
function setScrollTop (line 3952) | function setScrollTop(cm, val) {
function setScrollLeft (line 3963) | function setScrollLeft(cm, val, isScroller) {
function onScrollWheel (line 4007) | function onScrollWheel(cm, e) {
function doHandleBinding (line 4085) | function doHandleBinding(cm, bound, dropShift) {
function lookupKeyForEditor (line 4105) | function lookupKeyForEditor(cm, name, handle) {
function dispatchKey (line 4115) | function dispatchKey(cm, name, e, handle) {
function handleKeyBinding (line 4147) | function handleKeyBinding(cm, e) {
function handleCharBinding (line 4166) | function handleCharBinding(cm, e, ch) {
function onKeyDown (line 4172) | function onKeyDown(e) {
function showCrossHair (line 4193) | function showCrossHair(cm) {
function onKeyUp (line 4208) | function onKeyUp(e) {
function onKeyPress (line 4213) | function onKeyPress(e) {
function delayBlurEvent (line 4226) | function delayBlurEvent(cm) {
function onFocus (line 4236) | function onFocus(cm) {
function onBlur (line 4255) | function onBlur(cm) {
function onContextMenu (line 4272) | function onContextMenu(cm, e) {
function contextMenuInGutter (line 4278) | function contextMenuInGutter(cm, e) {
function adjustForChange (line 4295) | function adjustForChange(pos, change) {
function computeSelAfterChange (line 4304) | function computeSelAfterChange(doc, change) {
function offsetPos (line 4314) | function offsetPos(pos, old, nw) {
function computeReplacedSel (line 4323) | function computeReplacedSel(doc, changes, hint) {
function filterChange (line 4343) | function filterChange(doc, change, update) {
function makeChange (line 4367) | function makeChange(doc, change, ignoreReadOnly) {
function makeChangeInner (line 4389) | function makeChangeInner(doc, change) {
function makeChangeFromHistory (line 4407) | function makeChangeFromHistory(doc, type, allowSelectionOnly) {
function shiftDoc (line 4473) | function shiftDoc(doc, distance) {
function makeChangeSingleDoc (line 4489) | function makeChangeSingleDoc(doc, change, selAfter, spans) {
function makeChangeSingleDocInEditor (line 4522) | function makeChangeSingleDocInEditor(cm, change, spans) {
function replaceRange (line 4581) | function replaceRange(doc, code, from, to, origin) {
function maybeScrollWindow (line 4592) | function maybeScrollWindow(cm, coords) {
function scrollPosIntoView (line 4612) | function scrollPosIntoView(cm, pos, end, margin) {
function scrollIntoView (line 4636) | function scrollIntoView(cm, x1, y1, x2, y2) {
function calculateScrollPos (line 4646) | function calculateScrollPos(cm, x1, y1, x2, y2) {
function addToScrollPos (line 4676) | function addToScrollPos(cm, left, top) {
function ensureCursorVisible (line 4686) | function ensureCursorVisible(cm) {
function resolveScrollToPos (line 4700) | function resolveScrollToPos(cm) {
function indentLine (line 4720) | function indentLine(cm, n, how, aggressive) {
function changeLine (line 4782) | function changeLine(doc, handle, changeType, op) {
function deleteNearSelection (line 4793) | function deleteNearSelection(cm, compute) {
function findPosH (line 4825) | function findPosH(doc, pos, dir, unit, visually) {
function findPosV (line 4877) | function findPosV(cm, pos, dir, unit) {
function interpret (line 5298) | function interpret(val) {
function option (line 5354) | function option(name, deflt, handle, notOnInit) {
function normalizeKeyName (line 5821) | function normalizeKeyName(name) {
function getKeyMap (line 5907) | function getKeyMap(val) {
function save (line 5928) | function save() {textarea.value = cm.getValue();}
function markText (line 6178) | function markText(doc, from, to, options, type) {
function markTextShared (line 6271) | function markTextShared(doc, from, to, options, type) {
function findSharedMarkers (line 6286) | function findSharedMarkers(doc) {
function copySharedMarkers (line 6291) | function copySharedMarkers(doc, markers) {
function detachSharedMarkers (line 6303) | function detachSharedMarkers(markers) {
function MarkedSpan (line 6319) | function MarkedSpan(marker, from, to) {
function getMarkedSpanFor (line 6325) | function getMarkedSpanFor(spans, marker) {
function removeMarkedSpan (line 6333) | function removeMarkedSpan(spans, span) {
function addMarkedSpan (line 6339) | function addMarkedSpan(line, span) {
function markedSpansBefore (line 6348) | function markedSpansBefore(old, startCh, isInsert) {
function markedSpansAfter (line 6359) | function markedSpansAfter(old, endCh, isInsert) {
function stretchSpansOverChange (line 6378) | function stretchSpansOverChange(doc, change) {
function clearEmptySpans (line 6440) | function clearEmptySpans(spans) {
function mergeOldSpans (line 6454) | function mergeOldSpans(doc, change) {
function removeReadOnlyRanges (line 6477) | function removeReadOnlyRanges(doc, from, to) {
function detachMarkedSpans (line 6506) | function detachMarkedSpans(line) {
function attachMarkedSpans (line 6513) | function attachMarkedSpans(line, spans) {
function extraLeft (line 6522) | function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
function extraRight (line 6523) | function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
function compareCollapsedMarkers (line 6528) | function compareCollapsedMarkers(a, b) {
function collapsedSpanAtSide (line 6541) | function collapsedSpanAtSide(line, start) {
function collapsedSpanAtStart (line 6551) | function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, t...
function collapsedSpanAtEnd (line 6552) | function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, fal...
function conflictingCollapsedRange (line 6557) | function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
function visualLine (line 6577) | function visualLine(line) {
function visualLineContinued (line 6586) | function visualLineContinued(line) {
function visualLineNo (line 6597) | function visualLineNo(doc, lineN) {
function visualLineEndNo (line 6604) | function visualLineEndNo(doc, lineN) {
function lineIsHidden (line 6616) | function lineIsHidden(doc, line) {
function lineIsHiddenInner (line 6627) | function lineIsHiddenInner(doc, line, span) {
function adjustScrollWhenAboveVisible (line 6655) | function adjustScrollWhenAboveVisible(cm, line, diff) {
function widgetHeight (line 6684) | function widgetHeight(widget) {
function addLineWidget (line 6699) | function addLineWidget(doc, handle, node, options) {
function updateLine (line 6734) | function updateLine(line, text, markedSpans, estimateHeight) {
function cleanUpLine (line 6746) | function cleanUpLine(line) {
function extractLineClasses (line 6751) | function extractLineClasses(type, output) {
function callBlankLine (line 6765) | function callBlankLine(mode, state) {
function readToken (line 6772) | function readToken(mode, stream, state, inner) {
function takeToken (line 6782) | function takeToken(cm, pos, precise, asArray) {
function runMode (line 6804) | function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
function highlightLine (line 6845) | function highlightLine(cm, line, state, forceToEnd) {
function getLineStyles (line 6883) | function getLineStyles(cm, line, updateFrontier) {
function processLine (line 6899) | function processLine(cm, text, state, startAt) {
function interpretTokenStyle (line 6914) | function interpretTokenStyle(style, options) {
function buildLineContent (line 6926) | function buildLineContent(cm, lineView) {
function defaultSpecialCharPlaceholder (line 6983) | function defaultSpecialCharPlaceholder(ch) {
function buildToken (line 6992) | function buildToken(builder, text, style, startStyle, endStyle, title, c...
function splitSpaces (line 7050) | function splitSpaces(old) {
function buildTokenBadBidi (line 7059) | function buildTokenBadBidi(inner, order) {
function buildCollapsedSpan (line 7078) | function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
function insertLineContent (line 7095) | function insertLineContent(line, builder, styles) {
function isWholeLineUpdate (line 7168) | function isWholeLineUpdate(doc, change) {
function updateDoc (line 7174) | function updateDoc(doc, change, markedSpans, estimateHeight) {
function LeafChunk (line 7237) | function LeafChunk(lines) {
function BranchChunk (line 7277) | function BranchChunk(children) {
function linkedDocs (line 7750) | function linkedDocs(doc, f, sharedHistOnly) {
function attachDoc (line 7765) | function attachDoc(cm, doc) {
function getLine (line 7779) | function getLine(doc, n) {
function getBetween (line 7794) | function getBetween(doc, start, end) {
function getLines (line 7806) | function getLines(doc, from, to) {
function updateLineHeight (line 7814) | function updateLineHeight(line, height) {
function lineNo (line 7821) | function lineNo(line) {
function lineAtHeight (line 7835) | function lineAtHeight(chunk, h) {
function heightAtLine (line 7856) | function heightAtLine(lineObj) {
function getOrder (line 7878) | function getOrder(line) {
function History (line 7886) | function History(startGen) {
function historyChangeFromChange (line 7903) | function historyChangeFromChange(doc, change) {
function clearSelectionEvents (line 7912) | function clearSelectionEvents(array) {
function lastChangeEvent (line 7922) | function lastChangeEvent(hist, force) {
function addChangeToHistory (line 7937) | function addChangeToHistory(doc, change, selAfter, opId) {
function selectionEventCanBeMerged (line 7979) | function selectionEventCanBeMerged(doc, origin, prev, sel) {
function addSelectionToHistory (line 7992) | function addSelectionToHistory(doc, sel, opId, options) {
function pushSelectionToHistory (line 8014) | function pushSelectionToHistory(sel, dest) {
function attachLocalSpans (line 8021) | function attachLocalSpans(doc, change, from, to) {
function removeClearedSpans (line 8032) | function removeClearedSpans(spans) {
function getOldSpans (line 8042) | function getOldSpans(doc, change) {
function copyHistoryArray (line 8052) | function copyHistoryArray(events, newGroup, instantiateSel) {
function rebaseHistSelSingle (line 8077) | function rebaseHistSelSingle(pos, from, to, diff) {
function rebaseHistArray (line 8093) | function rebaseHistArray(array, from, to, diff) {
function rebaseHist (line 8121) | function rebaseHist(hist, change) {
function e_defaultPrevented (line 8140) | function e_defaultPrevented(e) {
function e_target (line 8145) | function e_target(e) {return e.target || e.srcElement;}
function e_button (line 8146) | function e_button(e) {
function getHandlers (line 8175) | function getHandlers(emitter, type, copy) {
function signalLater (line 8209) | function signalLater(emitter, type /*, values...*/) {
function fireOrphanDelayed (line 8226) | function fireOrphanDelayed() {
function signalDOMEvent (line 8235) | function signalDOMEvent(cm, e, override) {
function signalCursorActivity (line 8242) | function signalCursorActivity(cm) {
function hasHandler (line 8250) | function hasHandler(emitter, type) {
function eventMixin (line 8256) | function eventMixin(ctor) {
function Delayed (line 8273) | function Delayed() {this.id = null;}
function spaceStr (line 8313) | function spaceStr(n) {
function lst (line 8319) | function lst(arr) { return arr[arr.length-1]; }
function indexOf (line 8327) | function indexOf(array, elt) {
function map (line 8332) | function map(array, f) {
function nothing (line 8338) | function nothing() {}
function createObj (line 8340) | function createObj(base, props) {
function copyObj (line 8352) | function copyObj(obj, target, overwrite) {
function bind (line 8360) | function bind(f) {
function isWordChar (line 8370) | function isWordChar(ch, helper) {
function isEmpty (line 8376) | function isEmpty(obj) {
function isExtendingChar (line 8387) | function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendi...
function elt (line 8391) | function elt(tag, content, className, style) {
function removeChildren (line 8417) | function removeChildren(e) {
function removeChildrenAndAdd (line 8423) | function removeChildrenAndAdd(parent, e) {
function activeElt (line 8438) | function activeElt() {
function classTest (line 8451) | function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)...
function joinClasses (line 8464) | function joinClasses(a, b) {
function forEachCodeMirror (line 8477) | function forEachCodeMirror(f) {
function ensureGlobalHandlers (line 8487) | function ensureGlobalHandlers() {
function registerGlobalHandlers (line 8492) | function registerGlobalHandlers() {
function zeroWidthElement (line 8519) | function zeroWidthElement(measure) {
function hasBadBidiRects (line 8534) | function hasBadBidiRects(measure) {
function hasBadZoomedRects (line 8581) | function hasBadZoomedRects(measure) {
function iterateBidiSections (line 8612) | function iterateBidiSections(order, from, to, f) {
function bidiLeft (line 8625) | function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
function bidiRight (line 8626) | function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
function lineLeft (line 8628) | function lineLeft(line) { var order = getOrder(line); return order ? bid...
function lineRight (line 8629) | function lineRight(line) {
function lineStart (line 8635) | function lineStart(cm, lineN) {
function lineEnd (line 8643) | function lineEnd(cm, lineN) {
function lineStartSmart (line 8653) | function lineStartSmart(cm, pos) {
function compareBidiLevel (line 8665) | function compareBidiLevel(order, a, b) {
function getBidiPartAt (line 8672) | function getBidiPartAt(order, pos) {
function moveInLine (line 8692) | function moveInLine(line, pos, dir, byUnit) {
function moveVisually (line 8704) | function moveVisually(line, start, dir, byUnit) {
function moveLogically (line 8727) | function moveLogically(line, start, dir, byUnit) {
function charType (line 8761) | function charType(code) {
function BidiSpan (line 8776) | function BidiSpan(level, from, to) {
FILE: lib/codemirror/mode/javascript/javascript.js
function expressionAllowed (line 16) | function expressionAllowed(stream, state, backUp) {
function kw (line 32) | function kw(type) {return {type: type, style: "keyword"};}
function readRegexp (line 84) | function readRegexp(stream) {
function ret (line 99) | function ret(tp, style, cont) {
function tokenBase (line 103) | function tokenBase(stream, state) {
function tokenString (line 160) | function tokenString(quote) {
function tokenComment (line 176) | function tokenComment(stream, state) {
function tokenQuasi (line 188) | function tokenQuasi(stream, state) {
function findFatArrow (line 208) | function findFatArrow(stream, state) {
function JSLexical (line 238) | function JSLexical(indented, column, type, align, prev, info) {
function inScope (line 247) | function inScope(state, varname) {
function parseJS (line 256) | function parseJS(state, style, type, content, stream) {
function pass (line 280) | function pass() {
function cont (line 283) | function cont() {
function register (line 287) | function register(varname) {
function pushcontext (line 308) | function pushcontext() {
function popcontext (line 312) | function popcontext() {
function pushlex (line 316) | function pushlex(type, info) {
function poplex (line 327) | function poplex() {
function expect (line 337) | function expect(wanted) {
function statement (line 346) | function statement(type, value) {
function expression (line 373) | function expression(type) {
function expressionNoComma (line 376) | function expressionNoComma(type) {
function expressionInner (line 379) | function expressionInner(type, noComma) {
function maybeexpression (line 398) | function maybeexpression(type) {
function maybeexpressionNoComma (line 402) | function maybeexpressionNoComma(type) {
function maybeoperatorComma (line 407) | function maybeoperatorComma(type, value) {
function maybeoperatorNoComma (line 411) | function maybeoperatorNoComma(type, value, noComma) {
function quasi (line 426) | function quasi(type, value) {
function continueQuasi (line 431) | function continueQuasi(type) {
function arrowBody (line 438) | function arrowBody(type) {
function arrowBodyNoComma (line 442) | function arrowBodyNoComma(type) {
function maybeTarget (line 446) | function maybeTarget(noComma) {
function target (line 452) | function target(_, value) {
function targetNoComma (line 455) | function targetNoComma(_, value) {
function maybelabel (line 458) | function maybelabel(type) {
function property (line 462) | function property(type) {
function objprop (line 465) | function objprop(type, value) {
function getterSetter (line 483) | function getterSetter(type) {
function afterprop (line 488) | function afterprop(type) {
function commasep (line 492) | function commasep(what, end) {
function contCommasep (line 507) | function contCommasep(what, end, info) {
function block (line 512) | function block(type) {
function maybetype (line 516) | function maybetype(type) {
function maybedefault (line 519) | function maybedefault(_, value) {
function typeexpr (line 522) | function typeexpr(type) {
function afterType (line 525) | function afterType(type, value) {
function vardef (line 529) | function vardef() {
function pattern (line 532) | function pattern(type, value) {
function proppattern (line 539) | function proppattern(type, value) {
function maybeAssign (line 549) | function maybeAssign(_type, value) {
function vardefCont (line 552) | function vardefCont(type) {
function maybeelse (line 555) | function maybeelse(type, value) {
function forspec (line 558) | function forspec(type) {
function forspec1 (line 561) | function forspec1(type) {
function formaybeinof (line 567) | function formaybeinof(_type, value) {
function forspec2 (line 571) | function forspec2(type, value) {
function forspec3 (line 576) | function forspec3(type) {
function functiondef (line 579) | function functiondef(type, value) {
function funarg (line 584) | function funarg(type) {
function className (line 588) | function className(type, value) {
function classNameAfter (line 591) | function classNameAfter(type, value) {
function classBody (line 595) | function classBody(type, value) {
function classGetterSetter (line 612) | function classGetterSetter(type) {
function afterExport (line 617) | function afterExport(_type, value) {
function afterImport (line 622) | function afterImport(type) {
function importSpec (line 626) | function importSpec(type, value) {
function maybeAs (line 632) | function maybeAs(_type, value) {
function maybeFrom (line 635) | function maybeFrom(_type, value) {
function arrayLiteral (line 638) | function arrayLiteral(type) {
function maybeArrayComprehension (line 642) | function maybeArrayComprehension(type) {
function comprehension (line 647) | function comprehension(type) {
function isContinuedStatement (line 652) | function isContinuedStatement(state, textAfter) {
FILE: lib/codemirror/mode/javascript/test.js
function MT (line 6) | function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arg...
function LD (line 177) | function LD(name) {
FILE: lib/codemirror/mode/jsx/jsx.js
function Context (line 17) | function Context(state, mode, depth, prev) {
function copyContext (line 21) | function copyContext(context) {
function flatXMLIndent (line 32) | function flatXMLIndent(state) {
function token (line 40) | function token(stream, state) {
function xmlToken (line 47) | function xmlToken(stream, state, cx) {
function jsToken (line 105) | function jsToken(stream, state, cx) {
FILE: lib/codemirror/mode/jsx/test.js
function MT (line 6) | function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arg...
Condensed preview — 136 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,147K chars).
[
{
"path": ".gitignore",
"chars": 265,
"preview": "### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Dependency directories\nnode_modules\njspm_packages\n\n# Optional npm cache"
},
{
"path": "Gemfile",
"chars": 115,
"preview": "source 'https://rubygems.org'\ngem 'github-pages', group: :jekyll_plugins\ngem 'wdm', '>= 0.1.0' if Gem.win_platform?"
},
{
"path": "README.md",
"chars": 2340,
"preview": "\n# Shoutem\n\nShoutem is a platform for building beautiful React Native mobile apps. The easiest way to understand what Sh"
},
{
"path": "_config.yml",
"chars": 1321,
"preview": "title: Shoutem Developers\ndescription: Supercharge your React Native development with Shoutem\ntracking_id: UA-807293-5\nr"
},
{
"path": "_includes/cards.html",
"chars": 10652,
"preview": "\t\t\t\t\t<div class=\"section-content\">\n\t\t\t\t\t\t<div class=\"container screen-type-cards\">\n\t\t\t\t\t\t\t<h4 class=\"subtitle\">40+ full-"
},
{
"path": "_includes/footer.html",
"chars": 2151,
"preview": "<footer class=\"footer\">\n <div id=\"signup\" class=\"signup\">\n <div class=\"signup-title\">Stay informed</div>\n <div cl"
},
{
"path": "_includes/ga.html",
"chars": 415,
"preview": "<script>\n (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n (i[r].q=i[r].q||[]).push(argum"
},
{
"path": "_includes/head.html",
"chars": 2496,
"preview": "<head>\n <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n <meta charset=\"utf-8\">\n <meta name=\"view"
},
{
"path": "_includes/home-header-video.html",
"chars": 1227,
"preview": "<video autoplay loop class=\"video-screen\">\n\t<source src=\"/video/header.webm\" type=\"video/webm\" />\n\t<source src=\"/video/h"
},
{
"path": "_includes/navbar.html",
"chars": 2911,
"preview": "<nav class=\"navbar navbar-default navbar-fixed-top headroom\">\n\t<div class=\"container-fluid\">\n\n\t\t<div class=\"navbar-heade"
},
{
"path": "_includes/overview-content.html",
"chars": 5393,
"preview": "\t<h1>Develop native apps with Shoutem and React Native</h1>\n\n\t<div class=\"section section-intro\">\n\n\t\t<div class=\"intro-t"
},
{
"path": "_includes/sidebar-nav.html",
"chars": 10665,
"preview": "<ul class=\"sidebar-nav navigation level-1\" id=\"menu\">\n\n\t<li class=\"hidden-md hidden-lg home-link\"><a href=\"/\">Shoutem De"
},
{
"path": "_includes/signup-modal.html",
"chars": 1759,
"preview": "<div id=\"signup-modal\" role=\"dialog\" aria-labelledby=\"dialog-title\" aria-describedby=\"dialog-description\">\n <div class="
},
{
"path": "_layouts/doc.html",
"chars": 2034,
"preview": "<!DOCTYPE html>\n<html>\n\n{% include head.html %}\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.url }}/css/prism.cs"
},
{
"path": "_layouts/home.html",
"chars": 6708,
"preview": "<!DOCTYPE html>\n<html lang=\"en-us\">\n\n{% include head.html %}\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.url }}"
},
{
"path": "_layouts/overview.html",
"chars": 1755,
"preview": "<!DOCTYPE html>\n<html>\n\n{% include head.html %}\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{{ site.url }}/css/prism.cs"
},
{
"path": "_sass/animation.scss",
"chars": 1820,
"preview": "$imagesPath: '../img/';\n\n/* animation container */\n.shoutem-ani { \n position: absolute; \n z-index: 1; \n left: 0"
},
{
"path": "_sass/base.scss",
"chars": 1476,
"preview": "/* Bootstrap overrides */\nbody {\n color: $primary;\n line-height: 30px;\n}\nblockquote {\n border-width: 1px;\n f"
},
{
"path": "_sass/cards.scss",
"chars": 1409,
"preview": ".list-cards {\n display: flex;\n justify-content: space-between;\n margin-top: 40px;\n\n @include medium {\n "
},
{
"path": "_sass/colors.scss",
"chars": 548,
"preview": "/*Documentation*/\n$accent: #00aadf;\n$primary: #444f6c;\n$primaryhover: #4a5b85;\n$secondary: rgba(68, 79, 108, 0.2);\n$seco"
},
{
"path": "_sass/documentation-layout.scss",
"chars": 656,
"preview": ".container-fluid,\n.navbar .container-fluid\n{\n max-width: 1920px;\n}\n\n#wrapper {\n width: 100%;\n background: #FFFFFF"
},
{
"path": "_sass/footer.scss",
"chars": 6793,
"preview": ".footer {\n\n position: fixed; \n bottom: 0; \n width: 100%; \n z-index: -1;\n\n @include small {\n positi"
},
{
"path": "_sass/github.scss",
"chars": 2721,
"preview": "/*\n Copyright 2014 GitHub Inc.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use "
},
{
"path": "_sass/helpers.scss",
"chars": 84,
"preview": ".center-v {\n position: relative;\n top: 50%;\n transform: translateY(-45%);\n}"
},
{
"path": "_sass/menu-overlay.scss",
"chars": 1198,
"preview": "@import \"variables\";\n@import \"mixins\";\n\n.mobile-menu-overlay {\n position: fixed;\n visibility: hidden;\n top: 0;\n"
},
{
"path": "_sass/mixins.scss",
"chars": 2800,
"preview": "@mixin headline ($size, $lineheight, $color: $primary) {\n font-size: $size;\n line-height: $lineheight;\n color: $color"
},
{
"path": "_sass/navbar.scss",
"chars": 4526,
"preview": ".headroom {\n will-change: transform;\n transition: transform 200ms linear;\n}\n.headroom--pinned {\n transform: tra"
},
{
"path": "_sass/pager.scss",
"chars": 3378,
"preview": "#pager-wrapper\n{\n max-width: 1440px;\n margin: 80px auto 0;\n\n .pager {\n background: #FFFFFF;\n marg"
},
{
"path": "_sass/sidebar.scss",
"chars": 2186,
"preview": ".navigation {\n list-style: none;\n margin: 0;\n padding: 0;\n background: #FFF;\n\n ul {\n list-style: n"
},
{
"path": "_sass/typography.scss",
"chars": 2389,
"preview": "@font-face {\n font-family: 'SourceCode';\n src: url('../fonts/sourcecodepro-medium-webfont.eot');\n src: url('../"
},
{
"path": "_sass/variables.scss",
"chars": 84,
"preview": "$breakpoints: (\n 'small': 768px,\n 'medium': 992px,\n 'large': 1200px\n) !default;"
},
{
"path": "css/documentation.scss",
"chars": 16122,
"preview": "---\n---\n@import \"colors\";\n@import \"mixins\";\n@import \"documentation-layout\";\n@import \"sidebar\";\n@import \"pager\";\n@import "
},
{
"path": "css/home.scss",
"chars": 32799,
"preview": "---\n---\n\n@import \"colors\";\n@import \"variables\";\n@import \"mixins\";\n@import \"footer\";\n@import \"animation\";\n@import \"helper"
},
{
"path": "css/prism.css",
"chars": 3243,
"preview": "/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+bash+json+jsx&plugins=line-highli"
},
{
"path": "css/style.scss",
"chars": 118,
"preview": "---\n---\n@import \"colors\";\n@import \"mixins\";\n@import \"base\";\n@import \"navbar\";\n@import \"typography\";\n@import \"github\";\n"
},
{
"path": "csv/restaurants.csv",
"chars": 2089,
"preview": "Name,Address,Description,Website,Image,E-mail\n\"Gaspar Brasserie\",\"185 Sutter St, San Francisco, CA 94109\",\"Expect an int"
},
{
"path": "docs/cloud/_posts/1970-01-01-ShoutemCloud.md",
"chars": 1543,
"preview": "---\nlayout: doc\npermalink: /docs/cloud/introduction\ntitle: Introduction\nsection: Shoutem Cloud\n---\n\n# Shoutem Cloud\n\nSho"
},
{
"path": "docs/cloud/_posts/1970-01-02-DataSchemas.md",
"chars": 6976,
"preview": "---\nlayout: doc\npermalink: /docs/cloud/data-schemas\ntitle: Introduction\nsection: Data Schemas\n---\n\n# Data Schemas\n\nData "
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-01-Introduction.md",
"chars": 3182,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/introduction\ntitle: Introduction\nsection: My First Extens"
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-03-InitializingExtension.md",
"chars": 3901,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/initializing-extension\ntitle: Creating an Extension\nsecti"
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-04-CreatingShortcutAndScreen.md",
"chars": 6722,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/shortcut-and-screen\ntitle: Creating a Screen and Shortcut"
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-05-UsingUIToolkit.md",
"chars": 10696,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/using-ui-toolkit\ntitle: Using UI toolkit\nsection: My firs"
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-06-UsingCloudStorage.md",
"chars": 6095,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/using-cloud-storage\ntitle: Using Cloud Storage\nsection: M"
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-07-WorkingWithData.md",
"chars": 6322,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/working-with-data\ntitle: Working with Data\nsection: My fi"
},
{
"path": "docs/extensions/my-first-extension/_posts/1970-01-08-Publish.md",
"chars": 2308,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/my-first-extension/publish\ntitle: Publish your extension and app\nsection: My"
},
{
"path": "docs/extensions/reference/_posts/1970-01-01-ExtensionFile.md",
"chars": 12722,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/extension\ntitle: Extension file format\nsection: Reference\n---\n\n# E"
},
{
"path": "docs/extensions/reference/_posts/1970-01-01-Overview.md",
"chars": 10876,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/overview\ntitle: Technical overview\nsection: Extensions\n---\n\n# Tech"
},
{
"path": "docs/extensions/reference/_posts/1970-01-02-Platform.md",
"chars": 1834,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/platform\ntitle: Platform\nsection: Reference\n---\n\n# Platform\n<hr />"
},
{
"path": "docs/extensions/reference/_posts/1970-01-03-ExtensionExports.md",
"chars": 3687,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/extension-exports\ntitle: Extension exports\nsection: Reference\n---\n"
},
{
"path": "docs/extensions/reference/_posts/1970-01-04-SettingsTypesInExtension.md",
"chars": 10024,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/settings-types\ntitle: Settings types in extension\nsection: Referen"
},
{
"path": "docs/extensions/reference/_posts/1970-01-05-ThemeVariables.md",
"chars": 4895,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/theme-variables\ntitle: Theme variables\nsection: Reference\n---\n\n# T"
},
{
"path": "docs/extensions/reference/_posts/1970-01-06-CLI.md",
"chars": 15484,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/reference/cli\ntitle: Shoutem CLI\nsection: Extensions\n---\n\n# Shoutem CLI\nShou"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-01-ConnectToApi.md",
"chars": 4538,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/connecting-to-api\ntitle: Connecting to 3rd Party API\nsection: Tuto"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-01-GettingStarted.md",
"chars": 7402,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/getting-started\ntitle: Getting Started\nsection: Tutorials\n---\n\n# G"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-01-PublishYourApp.md",
"chars": 18717,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/publish-your-app\ntitle: Publish your app\nsection: Tutorials\n---\n\n#"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-01-SettingLocalEnvironment.md",
"chars": 4965,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/setting-local-environment\ntitle: Setting up your Local Environment"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-02-WritingATheme.md",
"chars": 19078,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/writing-a-theme\ntitle: Writing a Theme\nsection: Tutorials\n---\n\n# W"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-03-ScreenLayouts.md",
"chars": 7475,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/screen-layouts\ntitle: Screen Layouts\nsection: Tutorials\n---\n\n# Scr"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-04-WritingReactSettingsPage.md",
"chars": 15042,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/writing-react-settings-page\ntitle: Writing React settings pages\nse"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-05-Installing3rdPartyPackages.md",
"chars": 13727,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/installing-3rd-party-packages\ntitle: Installing 3rd Party Packages"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-06-UsingNativeModules.md",
"chars": 10512,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/using-native-api\ntitle: Using native API\nsection: Tutorials\n---\n\n#"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-07-ModifyingNativeProject.md",
"chars": 7906,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/modifying-native-project\ntitle: Modifying Root App\nsection: Tutori"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-08-ModifiyingExtensions.md",
"chars": 12824,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/modifying-extensions\ntitle: Modifying Extensions\nsection: Tutorial"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-09-FAQ.md",
"chars": 4235,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/faq\ntitle: FAQ\nsection: Frequently Asked Questions\n---\n\n# Frequent"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-10-DebugSettingsPages.md",
"chars": 4246,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/debug-settings-pages\ntitle: Debugging and local development of set"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-11-WritingHTMLSettingsPages.md",
"chars": 14037,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/writing-html-settings-page\ntitle: Writing HTML settings pages\nsect"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-12-SettingsPageIntro.md",
"chars": 3016,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/settings-pages-introduction\ntitle: Settings Pages\nsection: Tutoria"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-13-UsingLocalization.md",
"chars": 5983,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/using-localization\ntitle: Localization\nsection: Tutorials\n---\n\n# L"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-14-SettingUpInstagram.md",
"chars": 3651,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/setting-up-instagram\ntitle: Setting up Instagram\nsection: Tutorial"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-15-UsingPatchPackage.md",
"chars": 2536,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/using-patch-package\ntitle: Using patch-package with extensions\nsec"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-16-NavigationIntroduction.md",
"chars": 967,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/navigation-introduction\ntitle: Navigation\nsection: Tutorials\n---\n\n"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-17-NavigationBreakingChanges.md",
"chars": 6826,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/navigation-breaking-changes\ntitle: Breaking changes\nsection: Tutor"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-18-NavigationStacks.md",
"chars": 5117,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/navigation-stacks\ntitle: Navigation stacks\nsection: Tutorials\n---\n"
},
{
"path": "docs/extensions/tutorials/_posts/1970-01-19-NavigationScreenDecorators.md",
"chars": 1925,
"preview": "---\nlayout: doc\npermalink: /docs/extensions/tutorials/navigation-screen-decorators\ntitle: Screen decorators\nsection: Tut"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-02-Driver.md",
"chars": 2093,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/driver\ntitle: Driver\nsection: Animation\n---\n\n# Driver\n\nAn animatio"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-03-Animations.md",
"chars": 1152,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/introduction\ntitle: Introduction\nsection: Animation\n---\n\n# Animati"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-03-FadeIn.md",
"chars": 1115,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/fade-in\ntitle: FadeIn\nsection: Animation\n---\n\n# FadeIn\n\nFades in c"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-04-FadeOut.md",
"chars": 1087,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/fade-out\ntitle: FadeOut\nsection: Animation\n---\n\n# FadeOut\n\nFades o"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-05-ZoomIn.md",
"chars": 1196,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/zoom-in\ntitle: ZoomIn\nsection: Animation\n---\n\n# ZoomIn\n\nZooms in c"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-06-ZoomOut.md",
"chars": 1205,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/zoom-out\ntitle: ZoomOut\nsection: Animation\n---\n\n# ZoomOut\n\nZooms o"
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-07-Parallax.md",
"chars": 1570,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/parallax\ntitle: Parallax\nsection: Animation\n---\n\n# Parallax\n\nAdds "
},
{
"path": "docs/ui-toolkit/animation/_posts/1970-01-08-CombiningAnimations.md",
"chars": 2715,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/animation/combining-animations\ntitle: Combining animations\nsection: Animatio"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-01-Introduction.md",
"chars": 4709,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/introduction\ntitle: Introduction\nsection: UI toolkit\n---\n\n# Introduction\n\n!["
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-02-Typography.md",
"chars": 2283,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/typography\ntitle: Typography\nsection: UI toolkit\n---\n\n# Typograph"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-03-NavigationBar.md",
"chars": 13752,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/navigation-bar\ntitle: NavigationBar\nsection: UI toolkit\n---\n\n# Na"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-04-DropDownMenu.md",
"chars": 3990,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/dropdown-menu\ntitle: DropDownMenu\nsection: UI toolkit\n---\n\n# Drop"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-05-ListView.md",
"chars": 10714,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/list-view\ntitle: ListView\nsection: UI toolkit\n---\n\n# ListView\n\nLi"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-06-GridView.md",
"chars": 4283,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/grid-view\ntitle: GridView\nsection: UI toolkit\n---\n\n# GridView\n\nSi"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-07-Cards.md",
"chars": 2773,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/cards\ntitle: Cards\nsection: UI toolkit\n---\n\n# Cards\n\nCards have b"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-08-Dividers.md",
"chars": 2534,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/dividers\ntitle: Dividers\nsection: UI toolkit\n---\n\n# Dividers\n\nDiv"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-09-Rows.md",
"chars": 8112,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/rows\ntitle: Rows\nsection: UI toolkit\n---\n\n# Rows\n\n`Row` is a cont"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-10-Tiles.md",
"chars": 10635,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/tiles\ntitle: Tiles\nsection: UI toolkit\n---\n\n# Tiles\n\nTiles are a "
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-11-Spinner.md",
"chars": 879,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/spinner\ntitle: Spinner\nsection: UI toolkit\n---\n\n# Spinner\n`Spinne"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-12-Buttons.md",
"chars": 5059,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/buttons\ntitle: Buttons\nsection: UI toolkit\n---\n\n# Buttons\n\nButton"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-13-Image.md",
"chars": 6686,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/image\ntitle: Image\nsection: UI toolkit\n---\n\n# Image\n\nThis documen"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-14-Icons.md",
"chars": 16390,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/icons\ntitle: Icons\nsection: UI toolkit\n---\n\n# Icons\n\nThis documen"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-15-View.md",
"chars": 1861,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/view\ntitle: View\nsection: UI toolkit\n---\n\n# View\n\nView is a React"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-16-Screen.md",
"chars": 970,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/screen\ntitle: Screen\nsection: UI toolkit\n---\n\n# Screen\n\nScreen is"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-17-TouchableOpacity.md",
"chars": 1107,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/touchable-opacity\ntitle: Touchable Opacity\nsection: UI toolkit\n--"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-18-Headers.md",
"chars": 2196,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/headers\ntitle: Headers\nsection: UI toolkit\n---\n\n# Headers\n\nHeader"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-19-Overlay.md",
"chars": 4023,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/overlay\ntitle: Overlay\nsection: UI toolkit\n---\n\n# Overlay\n\n`Overl"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-20-Video.md",
"chars": 1569,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/video\ntitle: Video\nsection: UI toolkit\n---\n\n# Video\n\n`Video` comp"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-21-Lightbox.md",
"chars": 2037,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/lightbox\ntitle: Lightbox\nsection: UI toolkit\n---\n\n# Lightbox\n\nAll"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-22-RichMedia.md",
"chars": 1607,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/html\ntitle: HTML\nsection: UI toolkit\n---\n\n# Html\n\nThe `Html` comp"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-22-SimpleHtml.md",
"chars": 3008,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/simplehtml\ntitle: Simple HTML\nsection: UI toolkit\n---\n\n# SimpleHt"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-23-TextInput.md",
"chars": 1734,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/text-input\ntitle: TextInput\nsection: UI toolkit\n---\n\n# TextInput\n"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-24-ImagePreview.md",
"chars": 911,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/image-preview\ntitle: ImagePreview\nsection: UI toolkit\n---\n\n# Imag"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-25-ImageGallery.md",
"chars": 2520,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/image-gallery\ntitle: ImageGallery\nsection: UI toolkit\n---\n\n# Imag"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-26-InlineGallery.md",
"chars": 1515,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/inline-gallery\ntitle: InlineGallery\nsection: UI toolkit\n---\n\n# In"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-27-Switch.md",
"chars": 1516,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/switch\ntitle: Switch\nsection: UI toolkit\n---\n\n# Switch\n\nThe `Swit"
},
{
"path": "docs/ui-toolkit/components/_posts/1970-01-29-ImageBackground.md",
"chars": 3360,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/components/image-background\ntitle: ImageBackground\nsection: UI toolkit\n---\n\n"
},
{
"path": "docs/ui-toolkit/theme/1970-01-01-Theme.md",
"chars": 8074,
"preview": "---\nlayout: doc\npermalink: /docs/ui-toolkit/theme/introduction\ntitle: Introduction\nsection: UI toolkit Theme\n---\n\n# Them"
},
{
"path": "index.html",
"chars": 20,
"preview": "---\nlayout: home\n---"
},
{
"path": "jekyll-static.sh",
"chars": 50,
"preview": "jekyll serve --watch --incremental --limit_posts 1"
},
{
"path": "js/animation.js",
"chars": 4101,
"preview": "// shoutem animation\nfunction ShoutemAnimation(p_container_selector) {\n\n\tthis.$container = $(p_container_selector);\n\tthi"
},
{
"path": "js/docs.js",
"chars": 8629,
"preview": "$(function() {\n var currentLocation = getLocation();\n var $window = jQuery(window);\n var $document = jQuery(document)"
},
{
"path": "js/flourish.js",
"chars": 9017,
"preview": "function Flourish ( options )\n{\n\tvar defaults = {\n\t\tsaveHistoryEntry: true,\n\t\treplaceContents: true,\n\t\treplaceBodyClasse"
},
{
"path": "js/main.js",
"chars": 3607,
"preview": "// requestAnimFrame pseudo-polyfill\nwindow.requestAnimFrame = (function(){\nreturn window.requestAnimationFrame || windo"
},
{
"path": "js/prism.js",
"chars": 25660,
"preview": "/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+bash+json+jsx&plugins=line-highli"
},
{
"path": "lib/codemirror/codemirror.css",
"chars": 8148,
"preview": "/* BASICS */\n\n.CodeMirror {\n /* Set height, width, borders, and global font properties here */\n font-family: monospace"
},
{
"path": "lib/codemirror/codemirror.js",
"chars": 354656,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
},
{
"path": "lib/codemirror/mode/javascript/index.html",
"chars": 4193,
"preview": "<!doctype html>\n\n<title>CodeMirror: JavaScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc"
},
{
"path": "lib/codemirror/mode/javascript/javascript.js",
"chars": 28401,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
},
{
"path": "lib/codemirror/mode/javascript/json-ld.html",
"chars": 2150,
"preview": "<!doctype html>\n\n<title>CodeMirror: JSON-LD mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/do"
},
{
"path": "lib/codemirror/mode/javascript/test.js",
"chars": 7699,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
},
{
"path": "lib/codemirror/mode/javascript/typescript.html",
"chars": 1547,
"preview": "<!doctype html>\n\n<title>CodeMirror: TypeScript mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc"
},
{
"path": "lib/codemirror/mode/jsx/index.html",
"chars": 2376,
"preview": "<!doctype html>\n\n<title>CodeMirror: JSX mode</title>\n<meta charset=\"utf-8\"/>\n<link rel=stylesheet href=\"../../doc/docs.c"
},
{
"path": "lib/codemirror/mode/jsx/jsx.js",
"chars": 5087,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
},
{
"path": "lib/codemirror/mode/jsx/test.js",
"chars": 2966,
"preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
},
{
"path": "static/localization/en.json",
"chars": 50662,
"preview": "{\n \"shoutem\": {\n \"affiliate\": {\n \"level\": \"level\",\n \"numberOfPoints\": \"{{numberOfPoints}} points\",\n \""
}
]
// ... and 6 more files (download for full content)
About this extraction
This page contains the full source code of the shoutem/shoutem.github.io GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 136 files (1.0 MB), approximately 280.7k tokens, and a symbol index with 448 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.