Skip to content

Commit

Permalink
Separate static files & improved project (#11)
Browse files Browse the repository at this point in the history
* Improved project

* Make icon usage dynamic between use class name and use as icon file

* Improved example

* bump version

* Updated README

* format codes
  • Loading branch information
M97Chahboun authored Jan 16, 2025
1 parent 5c105f4 commit 8ac3cf6
Show file tree
Hide file tree
Showing 20 changed files with 271 additions and 293 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
django_icon_picker_example/db.sqlite3
django-icon-picker/build/**
django-icon-picker/django_icon_picker.egg-info/**
django_icon_picker_example/media/**
django-icon-picker/dist/**
43 changes: 5 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ Django Icon Picker is a custom widget for Django forms that allows users to sele

## Features

- **SVG File Support**: If the `DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH` is defined in your Django settings, the Icon Picker will download the selected SVG file and save it to the specified path. The path to the saved SVG file will be stored in the `in=con` field of the form.
- **Icon ID Support**: If the `DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH` is not defined, the Icon Picker will store the ID of the selected icon in the `icon` field.
- **SVG File Support**: If the `DJANGO_ICON_PICKER_PATH` is defined in your Django settings, the Icon Picker will download the selected SVG file and save it to the specified path. The path to the saved SVG file will be stored in the `in=con` field of the form.
- **Icon ID Support**: If the `DJANGO_ICON_PICKER_PATH` is not defined, the Icon Picker will store the ID of the selected icon in the `icon` field.
- **Easy Integration**: Use the `IconPicker` widget as a model field widget in your Django forms.

## Screenshot

![](icon_picker.gif)


## Usage

### Step 1: Install Django Icon Picker
Expand Down Expand Up @@ -47,39 +46,14 @@ urlpatterns = [

### Step 2: Configure Django Settings

If you want to use SVG files, define the `DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH` in your Django settings. This is the path where the SVG files will be saved.
If you want to use SVG files, define the `DJANGO_ICON_PICKER_PATH` in your Django settings. This is the path where the SVG files will be saved.

```python
# settings.py
DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH = '/path/to/save/svg/files'
```

### Step 3: Use IconPicker in Your Forms

To use the IconPicker widget in your forms, simply specify it as the widget for your icon field.

```python
from django import forms
from django_icon_picker.widgets import IconPicker
from .models import AttributeItem

class AttributeItemForm(forms.ModelForm):
icon = forms.CharField(widget=IconPicker())

# is required for download svg file approach
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['icon'].widget.attrs['model'] = self._meta.model.__name__
if self.instance and self.instance.pk:
object_id = self.instance.pk
self.fields['icon'].widget.attrs['objectid'] = object_id
else:
last_item_id = AttributeItem.objects.last().id if AttributeItem.objects.exists() else 1
next_id = last_item_id + 1
self.fields['icon'].widget.attrs['objectid'] = next_id
DJANGO_ICON_PICKER_PATH = 'media'
```

### Step 4: Override IconPicker Styling
### Step 3: Override IconPicker Styling

You can override the default styling of the `IconPicker` widget by subclassing it and modifying the `attrs` dictionary in the constructor. Here's an example:

Expand All @@ -89,13 +63,6 @@ from django_icon_picker.widgets import IconPicker

class CustomIconPicker(IconPicker):
def __init__(self, attrs: Dict[str, Any] | None = None) -> None:
attrs = attrs or {}
attrs["class"] = "max-w-2xl"
attrs["flex_class"] = "px-3 py-2 w-full max-w-2xl"
attrs["flex_input_class"] = "flex gap-2 items-center"
attrs["result_class"] = f"absolute flex flex-wrap max-w-md"
attrs["icons_class"] = "w-12 h-12 py-2 hover:bg-slate-600 rounded-md"
attrs["selected_icon"] = "w-8 h-8"
super().__init__(attrs)
```

Expand Down
5 changes: 4 additions & 1 deletion django-icon-picker/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
include README.md
include django_icon_picker/templates/django_icon_picker/icon_picker.html
recursive-include django_icon_picker/static *
recursive-include django_icon_picker/templates *
ecursive-exclude * *.pyc
exclude icon_picker.gif
43 changes: 5 additions & 38 deletions django-icon-picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ Django Icon Picker is a custom widget for Django forms that allows users to sele

## Features

- **SVG File Support**: If the `DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH` is defined in your Django settings, the Icon Picker will download the selected SVG file and save it to the specified path. The path to the saved SVG file will be stored in the `in=con` field of the form.
- **Icon ID Support**: If the `DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH` is not defined, the Icon Picker will store the ID of the selected icon in the `icon` field.
- **SVG File Support**: If the `DJANGO_ICON_PICKER_PATH` is defined in your Django settings, the Icon Picker will download the selected SVG file and save it to the specified path. The path to the saved SVG file will be stored in the `in=con` field of the form.
- **Icon ID Support**: If the `DJANGO_ICON_PICKER_PATH` is not defined, the Icon Picker will store the ID of the selected icon in the `icon` field.
- **Easy Integration**: Use the `IconPicker` widget as a model field widget in your Django forms.

## Screenshot

![](icon_picker.gif)


## Usage

### Step 1: Install Django Icon Picker
Expand Down Expand Up @@ -47,39 +46,14 @@ urlpatterns = [

### Step 2: Configure Django Settings

If you want to use SVG files, define the `DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH` in your Django settings. This is the path where the SVG files will be saved.
If you want to use SVG files, define the `DJANGO_ICON_PICKER_PATH` in your Django settings. This is the path where the SVG files will be saved.

```python
# settings.py
DJANGO_ICON_PICKER_SVG_FILES_SAVE_PATH = '/path/to/save/svg/files'
```

### Step 3: Use IconPicker in Your Forms

To use the IconPicker widget in your forms, simply specify it as the widget for your icon field.

```python
from django import forms
from django_icon_picker.widgets import IconPicker
from .models import AttributeItem

class AttributeItemForm(forms.ModelForm):
icon = forms.CharField(widget=IconPicker())

# is required for download svg file approach
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['icon'].widget.attrs['model'] = self._meta.model.__name__
if self.instance and self.instance.pk:
object_id = self.instance.pk
self.fields['icon'].widget.attrs['objectid'] = object_id
else:
last_item_id = AttributeItem.objects.last().id if AttributeItem.objects.exists() else 1
next_id = last_item_id + 1
self.fields['icon'].widget.attrs['objectid'] = next_id
DJANGO_ICON_PICKER_PATH = 'media'
```

### Step 4: Override IconPicker Styling
### Step 3: Override IconPicker Styling

You can override the default styling of the `IconPicker` widget by subclassing it and modifying the `attrs` dictionary in the constructor. Here's an example:

Expand All @@ -89,13 +63,6 @@ from django_icon_picker.widgets import IconPicker

class CustomIconPicker(IconPicker):
def __init__(self, attrs: Dict[str, Any] | None = None) -> None:
attrs = attrs or {}
attrs["class"] = "max-w-2xl"
attrs["flex_class"] = "px-3 py-2 w-full max-w-2xl"
attrs["flex_input_class"] = "flex gap-2 items-center"
attrs["result_class"] = f"absolute flex flex-wrap max-w-md"
attrs["icons_class"] = "w-12 h-12 py-2 hover:bg-slate-600 rounded-md"
attrs["selected_icon"] = "w-8 h-8"
super().__init__(attrs)
```

Expand Down
3 changes: 2 additions & 1 deletion django-icon-picker/django_icon_picker/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@


class DjangoIconPickerConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "django_icon_picker"
static_url = "/static/"
verbose_name = "Django Icon Picker"
8 changes: 5 additions & 3 deletions django-icon-picker/django_icon_picker/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from django.db import models
from .widgets import IconPicker


class IconField(models.CharField):
description = "A custom field to store icon information."

def __init__(self, *args, **kwargs):
kwargs['max_length'] = 255
kwargs["max_length"] = 255
super().__init__(*args, **kwargs)

def formfield(self, **kwargs):
kwargs['widget'] = IconPicker
return super().formfield(**kwargs)
attrs = {"model_name": self.model.__name__.lower()}
kwargs["widget"] = IconPicker(attrs=attrs)
return super().formfield(**kwargs)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.icon-dropdown-list {
max-height: 300px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.icon-dropdown-item {
display: flex;
align-items: center;
padding: 8px 12px;
cursor: pointer;
transition: background-color 0.2s;
}

.icon-dropdown-item:hover {
background-color: #f5f5f5;
}

.icon-preview {
width: 24px;
height: 24px;
margin-right: 12px;
}

.icon-name {
font-size: 14px;
color: #333;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
class IconPicker {
constructor(options) {
this.searchInput = document.getElementById(options.searchInputId);
this.selectedIcon = document.getElementById("selectedIcon");
this.resultsDiv = document.getElementById("results");
this.prefixDropdown = document.getElementById("prefixDropdown");
this.colorPicker = document.getElementById("color");
this.savePath = options.savePath;
this.selectedPrefix = "";
this.form = document.getElementById(`${options.model}_form`);
this.objectId = options.objectId;
this.model = options.model;
this.icon = "";

this.init();
}

init() {
this.setupInitialIcon();
this.setupEventListeners();
this.fetchIconifyPrefixes();
}

setupInitialIcon() {
this.selectedIcon.src = this.searchInput.value.endsWith(".svg")
? `/${this.searchInput.value}`
: `https://api.iconify.design/${this.searchInput.value}.svg`;
}

setupEventListeners() {
this.prefixDropdown.addEventListener("change", (event) => {
this.selectedPrefix = event.target.value;
});

this.searchInput.addEventListener("input", () => {
const query = this.searchInput.value;
if (query.length > 2) {
this.searchIcons(query);
} else {
this.resultsDiv.innerHTML = "";
}
});

this.form.addEventListener("submit", (event) => {
if (this.savePath) {
this.downloadAndSaveSvg(
`${this.icon}.svg&color=${this.colorPicker.value.replace("#", "%23")}`
);
}
this.form.submit();
});
}

async searchIcons(query) {
const response = await fetch(
`https://api.iconify.design/search?query=${encodeURIComponent(
query
)}&limit=10&start=0&prefix=${this.selectedPrefix}`
);
const data = await response.json();

this.resultsDiv.innerHTML = "";
if (data.icons.length > 0) {
const dropdownList = document.createElement("div");
dropdownList.className = "icon-dropdown-list";

data.icons.forEach((icon) => {
const iconName = icon.replace(":", "-");
const color = this.colorPicker.value.replace("#", "%23");
const iconBaseUrl = `https://api.iconify.design/${icon}.svg`;
const iconUrl = `${iconBaseUrl}?color=${color}`;

const dropdownItem = this.createDropdownItem(icon, iconUrl);
dropdownList.appendChild(dropdownItem);
});

this.resultsDiv.appendChild(dropdownList);
} else {
this.resultsDiv.textContent = "No icons found.";
}
}

createDropdownItem(icon, iconUrl) {
const item = document.createElement("div");
item.className = "icon-dropdown-item";

const iconImg = document.createElement("img");
iconImg.src = iconUrl;
iconImg.className = "icon-preview";

const iconText = document.createElement("span");
iconText.textContent = icon;
iconText.className = "icon-name";

item.appendChild(iconImg);
item.appendChild(iconText);

item.addEventListener("click", () => {
this.searchInput.value = icon;
this.selectedIcon.src = iconUrl;
this.resultsDiv.innerHTML = "";
this.icon = this.searchInput.value;
if (this.savePath) {
this.searchInput.value =
this.savePath + `/${this.model}/icon-${this.objectId}.svg`;
} else {
this.searchInput.value = icon;
}
});

return item;
}

fetchIconifyPrefixes() {
fetch("https://api.iconify.design/collections?pretty=1")
.then((response) => response.json())
.then((data) => {
const prefixes = Object.keys(data);
this.populatePrefixDropdown(prefixes);
})
.catch((error) => console.error("Error fetching icon sets:", error));
}

populatePrefixDropdown(prefixes) {
const selectElement = document.createElement("select");
selectElement.id = "prefixDropdown";
selectElement.className = "form-select block";

prefixes.forEach((prefix) => {
const option = document.createElement("option");
option.value = prefix;
option.textContent = prefix;
selectElement.appendChild(option);
});

this.prefixDropdown.appendChild(selectElement);
}

downloadAndSaveSvg(svgIcon) {
const downloadUrl = `/icon_picker/download-svg/?icon=${svgIcon}&id=${this.objectId}&model=${this.model}`;
fetch(downloadUrl)
.then((response) => response.text())
.then((data) => {
this.searchInput.value = data;
})
.catch((error) => {
console.error("Error:", error);
});
}
}
Loading

0 comments on commit 8ac3cf6

Please sign in to comment.