Full Code of dlerm/shopify-wishlist for AI

master e88dcf528ac5 cached
16 files
14.7 KB
4.1k tokens
5 symbols
1 requests
Download .txt
Repository: dlerm/shopify-wishlist
Branch: master
Commit: e88dcf528ac5
Files: 16
Total size: 14.7 KB

Directory structure:
gitextract_v887apka/

├── .gitignore
├── LICENSE
├── README.md
├── assets/
│   └── Wishlist.js
├── config.yml
├── gulpfile.js
├── package.json
├── sections/
│   ├── product-card-template.liquid
│   └── wishlist-template.liquid
├── snippets/
│   ├── button-wishlist.liquid
│   └── icon-heart.liquid
├── tasks/
│   ├── clean.js
│   ├── copy.js
│   └── upload.js
└── templates/
    ├── page.wishlist.json
    └── product.card.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# OS generated files #
######################
*.DS_Store*
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Helper files #
######################
_*

# Build tools #
######################
node_modules/
dist/
.env

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Daniel Lerman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Shopify Wishlist

A set of files used to implement a simple customer wishlist on a Shopify store.

_Version: 2.3.0_ - Compatible with Online Store 2.0

Previous Versions:
- [v2.1.0 (Online Store 1.0)](https://github.com/dlerm/shopify-wishlist/tree/version/2.1.0)
- [v2.0.0](https://github.com/dlerm/shopify-wishlist/tree/version/2.0.0)
- [v1.0.0](https://github.com/dlerm/shopify-wishlist/tree/version/1.0.0)

## Installation

To begin using **Shopify Wishlist**, you must copy some of the files in this repo into your Shopify theme code.

_Note_: This setup assumes that you have a snippet for displaying a product card.

**Files to copy:**
|Repo File|Shopify Theme Location|
|:--:|:--:|
|[`button-wishlist.liquid`](https://github.com/dlerm/shopify-wishlist/blob/master/snippets/button-wishlist.liquid)|`snippets/`|
|[`icon-heart.liquid`](https://github.com/dlerm/shopify-wishlist/blob/master/snippets/icon-heart.liquid)| `snippets/`|
|[`wishlist-template.liquid`](https://github.com/dlerm/shopify-wishlist/blob/master/sections/wishlist-template.liquid)| `sections/`|
|[`product-card-template.liquid`](https://github.com/dlerm/shopify-wishlist/blob/master/sections/product-card-template.liquid)| `sections/`|
|[`page.wishlist.json`](https://github.com/dlerm/shopify-wishlist/blob/master/templates/page.wishlist.json)|`templates/`|
|[`product.card.json`](https://github.com/dlerm/shopify-wishlist/blob/master/templates/product.card.json)|`templates/`|
|[`Wishlist.js`](https://github.com/dlerm/shopify-wishlist/blob/master/assets/Wishlist.js)|`assets/`|

1. Place the `button-wishlist.liquid` snippet inside your existing product card snippet, or on the `product.liquid` template
   - `{%- render 'button-wishlist', product: product -%}`
   - This will allow customer's to add/remove items to/from their wishlist
2. Replace the snippet in the `product-card-template.liquid` section with your existing product card snippet
   - Same snippet from step 1
   - Your product card snippet may use a different product variable name, double check it :+1:
3. Add your product card element classname to the `selectors` object in `Wishlist.js`
   - Example: If your product card classname is `.product-item`, the `selectors` variable would look like this:
      ```
      const selectors = {
        button: '[button-wishlist]',
        grid: '[grid-wishlist]',
        productCard: '.product-item', // your classname here
      };
      ```
4. Create a new page in the Shopify admin:
   - Admin > Online Store > Pages > Add Page
   - Set the new page's `template` to `page.wishlist`
   - This page will display a customer's saved wishlist items
5. Place the script in `theme.liquid` before the closing `</head>` tag
   - `<script src="{{ 'Wishlist.js' | asset_url }}" defer="defer"></script>`

That's it! When viewing your Shopify store, you should see the wishlist buttons inside your product cards (likely on collections pages) or on the product template. A click on the wishlist button will add/remove the item from the customer's wishlist and trigger active styling on the button. After adding wishlist items, you can view your wishlist by navigating to the page created in step 3.

[Demo Shopify Store](https://lerman-labs.myshopify.com/collections/all)

## Notes

- This wishlist uses Javascript and localStorage to save the customer's wishlist on their browser. The localStorage will not persist if the user clears browser storage or browses in incognito mode.
- As customers browser products and adds them to their wishlist, the script will automatically set any wishlist buttons to active state if the corresponding product is already included in the wishlist.
- These files come with no styling or structure so that you can customize as needed. This is intended to bring you the base functionality of a wishlist with no frills.
- If you are working in an unpublished theme, you will need to create the new templates on the published theme as well. The Shopify admin will only allow you to assign a page to a template if the template exists on the published theme.
- If you are upgrading to the Online Store 2.0 version, you will be required to delete the older `.liquid` wishlist and product card templates.


================================================
FILE: assets/Wishlist.js
================================================
const LOCAL_STORAGE_WISHLIST_KEY = 'shopify-wishlist';
const LOCAL_STORAGE_DELIMITER = ',';
const BUTTON_ACTIVE_CLASS = 'active';
const GRID_LOADED_CLASS = 'loaded';

const selectors = {
  button: '[button-wishlist]',
  grid: '[grid-wishlist]',
  productCard: '.product-card',
};

document.addEventListener('DOMContentLoaded', () => {
  initButtons();
  initGrid();
});

document.addEventListener('shopify-wishlist:updated', (event) => {
  console.log('[Shopify Wishlist] Wishlist Updated ✅', event.detail.wishlist);
  initGrid();
});

document.addEventListener('shopify-wishlist:init-product-grid', (event) => {
  console.log('[Shopify Wishlist] Wishlist Product List Loaded ✅', event.detail.wishlist);
});

document.addEventListener('shopify-wishlist:init-buttons', (event) => {
  console.log('[Shopify Wishlist] Wishlist Buttons Loaded ✅', event.detail.wishlist);
});

const fetchProductCardHTML = (handle) => {
  const productTileTemplateUrl = `/products/${handle}?view=card`;
  return fetch(productTileTemplateUrl)
  .then((res) => res.text())
  .then((res) => {
    const text = res;
    const parser = new DOMParser();
    const htmlDocument = parser.parseFromString(text, 'text/html');
    const productCard = htmlDocument.documentElement.querySelector(selectors.productCard);
    return productCard.outerHTML;
  })
  .catch((err) => console.error(`[Shopify Wishlist] Failed to load content for handle: ${handle}`, err));
};

const setupGrid = async (grid) => {
  const wishlist = getWishlist();
  const requests = wishlist.map(fetchProductCardHTML);
  const responses = await Promise.all(requests);
  const wishlistProductCards = responses.join('');
  grid.innerHTML = wishlistProductCards;
  grid.classList.add(GRID_LOADED_CLASS);
  initButtons();

  const event = new CustomEvent('shopify-wishlist:init-product-grid', {
    detail: { wishlist: wishlist }
  });
  document.dispatchEvent(event);
};

const setupButtons = (buttons) => {
  buttons.forEach((button) => {
    const productHandle = button.dataset.productHandle || false;
    if (!productHandle) return console.error('[Shopify Wishlist] Missing `data-product-handle` attribute. Failed to update the wishlist.');
    if (wishlistContains(productHandle)) button.classList.add(BUTTON_ACTIVE_CLASS);
    button.addEventListener('click', () => {
      updateWishlist(productHandle);
      button.classList.toggle(BUTTON_ACTIVE_CLASS);
    });
  });
};

const initGrid = () => {
  const grid = document.querySelector(selectors.grid) || false;
  if (grid) setupGrid(grid);
};

const initButtons = () => {
  const buttons = document.querySelectorAll(selectors.button) || [];
  if (buttons.length) setupButtons(buttons);
  else return;
  const event = new CustomEvent('shopify-wishlist:init-buttons', {
    detail: { wishlist: getWishlist() }
  });
  document.dispatchEvent(event);
};

const getWishlist = () => {
  const wishlist = localStorage.getItem(LOCAL_STORAGE_WISHLIST_KEY) || false;
  if (wishlist) return wishlist.split(LOCAL_STORAGE_DELIMITER);
  return [];
};

const setWishlist = (array) => {
  const wishlist = array.join(LOCAL_STORAGE_DELIMITER);
  if (array.length) localStorage.setItem(LOCAL_STORAGE_WISHLIST_KEY, wishlist);
  else localStorage.removeItem(LOCAL_STORAGE_WISHLIST_KEY);

  const event = new CustomEvent('shopify-wishlist:updated', {
    detail: { wishlist: array }
  });
  document.dispatchEvent(event);

  return wishlist;
};

const updateWishlist = (handle) => {
  const wishlist = getWishlist();
  const indexInWishlist = wishlist.indexOf(handle);
  if (indexInWishlist === -1) wishlist.push(handle);
  else wishlist.splice(indexInWishlist, 1);
  return setWishlist(wishlist);
};

const wishlistContains = (handle) => {
  const wishlist = getWishlist();
  return wishlist.includes(handle);
};

const resetWishlist = () => {
  return setWishlist([]);
};


================================================
FILE: config.yml
================================================
development:
  password: ${PASSWORD}
  theme_id: ${THEME_ID}
  store: ${URL}


================================================
FILE: gulpfile.js
================================================
const gulp = require('gulp');

require('require-dir')('./tasks');

// build
gulp.task(
  'build',
  gulp.series('clean:dist', 'copy')
);

// watch
gulp.task(
  'watch',
  gulp.series(
    gulp.parallel(
      'copy:watch',
    ),
    gulp.series('upload:watch')
  )
);

// dev
gulp.task('dev', gulp.series('build', 'watch'));

// default
gulp.task('default', gulp.series('dev'));


================================================
FILE: package.json
================================================
{
  "name": "shopify-wishlist",
  "version": "2.3.0",
  "author": "Daniel Lerman",
  "description": "💙 A set of files used to implement a simple customer wishlist on a Shopify store",
  "keywords": [
    "shopify",
    "theme",
    "wishlist"
  ],
  "bugs": "https://github.com/dlerm/shopify-wishlist/issues",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/dlerm/shopify-wishlist"
  },
  "scripts": {
    "start": "gulp",
    "build": "gulp build"
  },
  "dependencies": {},
  "devDependencies": {
    "@shopify/themekit": "^1.1.9",
    "dotenv": "^9.0.0",
    "gulp": "^4.0.2",
    "gulp-changed": "^4.0.2",
    "gulp-clean": "^0.4.0",
    "gulp-flatten": "^0.4.0",
    "gulp-if": "^3.0.0",
    "gulp-rename": "^2.0.0",
    "gulp-watch": "^5.0.1",
    "require-dir": "^1.2.0"
  }
}


================================================
FILE: sections/product-card-template.liquid
================================================
{% comment %}
  Shopify Wishlist
  Usage:
    - Replace the snippet name ('product-card') with your existing product card snippet
    - Pass the product object as a snippet variable named 'product'
{% endcomment %}
{%- render 'product-card', product: product -%}

{% schema %}
  {
    "templates": ["product"]
  }
{% endschema %}

================================================
FILE: sections/wishlist-template.liquid
================================================
{% comment %}
  Shopify Wishlist
  Usage:
    - Create a new page in the Shopify admin
      - Admin > Online store > Pages > Add page
    - Set the new page's template to: 'page.wishlist'
    - Do NOT remove the `grid-wishlist` attribute
  
  Notes:
  - The grid will be populated with product cards using Javascript
  - Any content inside of the `[grid-wishlist]` element will be completely replaced by the product cards
  
  Tip:
  - Place a loading element inside the `[grid-wishlist]` element and it will automatically be removed once the product cards have loaded
  - Add any liquid code before/after the grid element
{% endcomment %}
<div class="wishlist__grid flex container" grid-wishlist>
  {% comment %} Sample loading element {% endcomment %}
  <p class="wishlist__loader full-width text-center">Loading...</p>
</div>

{% schema %}
  {
    "templates": ["page"]
  }
{% endschema %}

================================================
FILE: snippets/button-wishlist.liquid
================================================
{% comment %}
  Shopify Wishlist
  Usage:
    - Markup: {%- render 'button-wishlist', product: product -%}
    - Place this snippet inside your existing product card snippet

  Parameters:
    - product: <Shopify product> (required)
{% endcomment %}
{%- if product.handle != blank -%}
  <button type="button" aria-label="Add to wishlist" class="" button-wishlist data-product-handle="{{ product.handle }}">
    <style scoped>
      .icon {
        fill: transparent;
        stroke: #000000;
        transition: fill 0.3s ease;
      }

      .active .icon {
        fill: red;
        stroke: #000000;
      }
    </style>
    {%- render 'icon-heart' -%}
  </button>
{%- endif -%}


================================================
FILE: snippets/icon-heart.liquid
================================================
{% comment %}
  Shopify Wishlist
  Usage:
    - Markup: {%- render 'icon-heart', class: product -%}
    - Place this snippet inside your existing product card snippet

  Parameters:
    - class: Add a css class for custom styling (optional)
{% endcomment %}
<svg class="icon icon-heart {{ class -}}" viewBox="0 0 290 256" xmlns="http://www.w3.org/2000/svg"><path d="M258.844192 127.790368L145 241.63456 31.1558082 127.790368c-26.9461761-26.946176-26.9461761-70.6345598 0-97.5807359 26.9461762-26.94617613 70.6345598-26.94617613 97.5807358 0L145 46.4730881l16.263456-16.263456c26.946176-26.94617613 70.63456-26.94617613 97.580736 0 26.946176 26.9461761 26.946176 70.6345599 0 97.5807359z" stroke="#000" stroke-width="20" fill="red" fill-rule="evenodd"/></svg>

================================================
FILE: tasks/clean.js
================================================
const gulp = require('gulp');
const clean = require('gulp-clean');
const fs = require('fs');

gulp.task('clean:dist', function (done) {
  if (fs.existsSync('dist')) return gulp.src(['dist'], { read: false }).pipe(clean());
  else done();
});


================================================
FILE: tasks/copy.js
================================================
const gulp = require('gulp');
const changed = require('gulp-changed');
const watch = require('gulp-watch');
const rename = require('gulp-rename');

const liquidPaths = [
  '*assets/**/*',
  '*snippets/**/*',
  '*sections/**/*',
  '*templates/**/*',
  '*layout/**/*',
];

function renameHiddenFiles (path) {
  if (path.basename[0] === '_') path.basename = path.basename.substring(1);
};

gulp.task('copy', function () {
  return gulp
    .src(liquidPaths)
    .pipe(rename(renameHiddenFiles))
    .pipe(changed('dist/', { hasChanged: changed.compareContents }))
    .pipe(gulp.dest('dist/'));
});

gulp.task('copy:watch', function (done) {
  watch(liquidPaths)
    .pipe(rename(renameHiddenFiles))
    .pipe(gulp.dest('dist/'));
  done();
});


================================================
FILE: tasks/upload.js
================================================
const gulp = require('gulp');
const path = require('path');
const themeKit = require('@shopify/themekit');

const options = {
  vars: path.resolve(__dirname, '../.env'),
  dir: path.resolve(__dirname, '../dist'),
};

gulp.task("upload:deploy", function () {
  themeKit.command('deploy', options);
});

gulp.task("upload:watch", function () {
  themeKit.command('watch', options);
});


================================================
FILE: templates/page.wishlist.json
================================================
{
  "name": "Wishlist template",
  "wrapper": "section#wishlist.wishlist",
  "sections": {
    "main": {
      "type": "wishlist-template"
    }
  },
  "order": [
    "main"
  ]
}

================================================
FILE: templates/product.card.json
================================================
{
  "name": "Product card template",
  "layout": false,
  "sections": {
    "main": {
      "type": "product-card-template"
    }
  },
  "order": [
    "main"
  ]
}
Download .txt
gitextract_v887apka/

├── .gitignore
├── LICENSE
├── README.md
├── assets/
│   └── Wishlist.js
├── config.yml
├── gulpfile.js
├── package.json
├── sections/
│   ├── product-card-template.liquid
│   └── wishlist-template.liquid
├── snippets/
│   ├── button-wishlist.liquid
│   └── icon-heart.liquid
├── tasks/
│   ├── clean.js
│   ├── copy.js
│   └── upload.js
└── templates/
    ├── page.wishlist.json
    └── product.card.json
Download .txt
SYMBOL INDEX (5 symbols across 2 files)

FILE: assets/Wishlist.js
  constant LOCAL_STORAGE_WISHLIST_KEY (line 1) | const LOCAL_STORAGE_WISHLIST_KEY = 'shopify-wishlist';
  constant LOCAL_STORAGE_DELIMITER (line 2) | const LOCAL_STORAGE_DELIMITER = ',';
  constant BUTTON_ACTIVE_CLASS (line 3) | const BUTTON_ACTIVE_CLASS = 'active';
  constant GRID_LOADED_CLASS (line 4) | const GRID_LOADED_CLASS = 'loaded';

FILE: tasks/copy.js
  function renameHiddenFiles (line 14) | function renameHiddenFiles (path) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
  {
    "path": ".gitignore",
    "chars": 228,
    "preview": "# OS generated files #\n######################\n*.DS_Store*\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n"
  },
  {
    "path": "LICENSE",
    "chars": 1070,
    "preview": "MIT License\n\nCopyright (c) 2021 Daniel Lerman\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
  },
  {
    "path": "README.md",
    "chars": 4210,
    "preview": "# Shopify Wishlist\n\nA set of files used to implement a simple customer wishlist on a Shopify store.\n\n_Version: 2.3.0_ - "
  },
  {
    "path": "assets/Wishlist.js",
    "chars": 3849,
    "preview": "const LOCAL_STORAGE_WISHLIST_KEY = 'shopify-wishlist';\nconst LOCAL_STORAGE_DELIMITER = ',';\nconst BUTTON_ACTIVE_CLASS = "
  },
  {
    "path": "config.yml",
    "chars": 77,
    "preview": "development:\n  password: ${PASSWORD}\n  theme_id: ${THEME_ID}\n  store: ${URL}\n"
  },
  {
    "path": "gulpfile.js",
    "chars": 380,
    "preview": "const gulp = require('gulp');\n\nrequire('require-dir')('./tasks');\n\n// build\ngulp.task(\n  'build',\n  gulp.series('clean:d"
  },
  {
    "path": "package.json",
    "chars": 824,
    "preview": "{\n  \"name\": \"shopify-wishlist\",\n  \"version\": \"2.3.0\",\n  \"author\": \"Daniel Lerman\",\n  \"description\": \"💙 A set of files us"
  },
  {
    "path": "sections/product-card-template.liquid",
    "chars": 329,
    "preview": "{% comment %}\n  Shopify Wishlist\n  Usage:\n    - Replace the snippet name ('product-card') with your existing product car"
  },
  {
    "path": "sections/wishlist-template.liquid",
    "chars": 893,
    "preview": "{% comment %}\n  Shopify Wishlist\n  Usage:\n    - Create a new page in the Shopify admin\n      - Admin > Online store > Pa"
  },
  {
    "path": "snippets/button-wishlist.liquid",
    "chars": 682,
    "preview": "{% comment %}\n  Shopify Wishlist\n  Usage:\n    - Markup: {%- render 'button-wishlist', product: product -%}\n    - Place t"
  },
  {
    "path": "snippets/icon-heart.liquid",
    "chars": 758,
    "preview": "{% comment %}\n  Shopify Wishlist\n  Usage:\n    - Markup: {%- render 'icon-heart', class: product -%}\n    - Place this sni"
  },
  {
    "path": "tasks/clean.js",
    "chars": 242,
    "preview": "const gulp = require('gulp');\nconst clean = require('gulp-clean');\nconst fs = require('fs');\n\ngulp.task('clean:dist', fu"
  },
  {
    "path": "tasks/copy.js",
    "chars": 742,
    "preview": "const gulp = require('gulp');\nconst changed = require('gulp-changed');\nconst watch = require('gulp-watch');\nconst rename"
  },
  {
    "path": "tasks/upload.js",
    "chars": 384,
    "preview": "const gulp = require('gulp');\nconst path = require('path');\nconst themeKit = require('@shopify/themekit');\n\nconst option"
  },
  {
    "path": "templates/page.wishlist.json",
    "chars": 179,
    "preview": "{\n  \"name\": \"Wishlist template\",\n  \"wrapper\": \"section#wishlist.wishlist\",\n  \"sections\": {\n    \"main\": {\n      \"type\": \""
  },
  {
    "path": "templates/product.card.json",
    "chars": 164,
    "preview": "{\n  \"name\": \"Product card template\",\n  \"layout\": false,\n  \"sections\": {\n    \"main\": {\n      \"type\": \"product-card-templa"
  }
]

About this extraction

This page contains the full source code of the dlerm/shopify-wishlist GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (14.7 KB), approximately 4.1k tokens, and a symbol index with 5 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.

Copied to clipboard!